From ef6ee160062a8d08ab2b12b788e081f518f4c7c6 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Fri, 17 Apr 2020 04:42:05 +0800 Subject: Fix path for proc-macro in nightly / stable build --- crates/rust-analyzer/src/cli/load_cargo.rs | 4 +--- crates/rust-analyzer/src/config.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 32a9ee339..018be70ee 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -73,9 +73,7 @@ pub(crate) fn load_cargo( let proc_macro_client = if !with_proc_macro { ProcMacroClient::dummy() } else { - let mut path = std::env::current_exe()?; - path.pop(); - path.push("rust-analyzer"); + let path = std::env::current_exe()?; ProcMacroClient::extern_process(&path, &["proc-macro"]).unwrap() }; let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs, &proc_macro_client); diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 2b45f1310..3597a14e3 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -134,9 +134,7 @@ impl Config { match get::(value, "/procMacro/enabled") { Some(true) => { - if let Ok(mut path) = std::env::current_exe() { - path.pop(); - path.push("rust-analyzer"); + if let Ok(path) = std::env::current_exe() { self.proc_macro_srv = Some((path.to_string_lossy().to_string(), vec!["proc-macro".to_string()])); } } -- cgit v1.2.3 From 02b96d522cc50252b4cb7927cae04248ea6b6193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Fri, 17 Apr 2020 11:12:05 +0300 Subject: Reduce allocations when looking up proc macro decl --- crates/ra_proc_macro_srv/src/dylib.rs | 72 +++++++++++++++++------------------ 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs index ec63d587b..7d6e5d323 100644 --- a/crates/ra_proc_macro_srv/src/dylib.rs +++ b/crates/ra_proc_macro_srv/src/dylib.rs @@ -16,55 +16,53 @@ fn invalid_data_err(e: impl Into>) -> I IoError::new(IoErrorKind::InvalidData, e) } -fn get_symbols_from_lib(file: &Path) -> Result, IoError> { +fn is_derive_registrar_symbol(symbol: &str) -> bool { + symbol.contains(NEW_REGISTRAR_SYMBOL) +} + +fn find_registrar_symbol(file: &Path) -> Result, IoError> { let buffer = std::fs::read(file)?; let object = Object::parse(&buffer).map_err(invalid_data_err)?; match object { Object::Elf(elf) => { let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?; - let names = symbols.iter().map(|s| s.to_string()).collect(); - Ok(names) + let name = + symbols.iter().find(|s| is_derive_registrar_symbol(s)).map(|s| s.to_string()); + Ok(name) } Object::PE(pe) => { - let symbol_names = - pe.exports.iter().flat_map(|s| s.name).map(|n| n.to_string()).collect(); - Ok(symbol_names) + let name = pe + .exports + .iter() + .flat_map(|s| s.name) + .find(|s| is_derive_registrar_symbol(s)) + .map(|s| s.to_string()); + Ok(name) } - Object::Mach(mach) => match mach { - Mach::Binary(binary) => { - let exports = binary.exports().map_err(invalid_data_err)?; - let names = exports - .into_iter() - .map(|s| { - // In macos doc: - // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html - // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be - // prepended with an underscore. - if s.name.starts_with("_") { - s.name[1..].to_string() - } else { - s.name - } - }) - .collect(); - Ok(names) - } - Mach::Fat(_) => Ok(vec![]), - }, - Object::Archive(_) | Object::Unknown(_) => Ok(vec![]), + Object::Mach(Mach::Binary(binary)) => { + let exports = binary.exports().map_err(invalid_data_err)?; + let name = exports + .iter() + .map(|s| { + // In macos doc: + // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html + // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be + // prepended with an underscore. + if s.name.starts_with("_") { + &s.name[1..] + } else { + &s.name + } + }) + .find(|s| is_derive_registrar_symbol(&s)) + .map(|s| s.to_string()); + Ok(name) + } + _ => Ok(None), } } -fn is_derive_registrar_symbol(symbol: &str) -> bool { - symbol.contains(NEW_REGISTRAR_SYMBOL) -} - -fn find_registrar_symbol(file: &Path) -> Result, IoError> { - let symbols = get_symbols_from_lib(file)?; - Ok(symbols.into_iter().find(|s| is_derive_registrar_symbol(s))) -} - /// Loads dynamic library in platform dependent manner. /// /// For unix, you have to use RTLD_DEEPBIND flag to escape problems described -- cgit v1.2.3 From 828f69ce549c9e07dec528247c97507749b1aec7 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 17 Apr 2020 10:32:12 +0200 Subject: tests: add more info about what failed in tidy tests Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- xtask/tests/tidy-tests/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xtask/tests/tidy-tests/main.rs b/xtask/tests/tidy-tests/main.rs index 101ae19bd..ead642acc 100644 --- a/xtask/tests/tidy-tests/main.rs +++ b/xtask/tests/tidy-tests/main.rs @@ -35,7 +35,7 @@ fn check_todo(path: &Path, text: &str) { } if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") { panic!( - "\nTODO markers should not be committed to the master branch,\n\ + "\nTODO markers or todo! macros should not be committed to the master branch,\n\ use FIXME instead\n\ {}\n", path.display(), @@ -47,9 +47,9 @@ fn check_trailing_ws(path: &Path, text: &str) { if is_exclude_dir(path, &["test_data"]) { return; } - for line in text.lines() { + for (line_number, line) in text.lines().enumerate() { if line.chars().last().map(char::is_whitespace) == Some(true) { - panic!("Trailing whitespace in {}", path.display()) + panic!("Trailing whitespace in {} at line {}", path.display(), line_number) } } } -- cgit v1.2.3 From 93fcf1c133f1a473ac598e4811e88ee91d979510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Fri, 17 Apr 2020 10:47:15 +0300 Subject: Use mmap for proc macro libs --- Cargo.lock | 11 +++++++++++ crates/ra_proc_macro_srv/Cargo.toml | 1 + crates/ra_proc_macro_srv/src/dylib.rs | 7 +++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 89a734c9b..3826ae1c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -675,6 +675,16 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi 0.3.8", +] + [[package]] name = "memoffset" version = "0.5.4" @@ -1112,6 +1122,7 @@ dependencies = [ "difference", "goblin", "libloading", + "memmap", "ra_mbe", "ra_proc_macro", "ra_tt", diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml index 1e0f50339..ac2d156dc 100644 --- a/crates/ra_proc_macro_srv/Cargo.toml +++ b/crates/ra_proc_macro_srv/Cargo.toml @@ -14,6 +14,7 @@ ra_mbe = { path = "../ra_mbe" } ra_proc_macro = { path = "../ra_proc_macro" } goblin = "0.2.1" libloading = "0.6.0" +memmap = "0.7" test_utils = { path = "../test_utils" } [dev-dependencies] diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs index 7d6e5d323..16bd7466e 100644 --- a/crates/ra_proc_macro_srv/src/dylib.rs +++ b/crates/ra_proc_macro_srv/src/dylib.rs @@ -1,10 +1,12 @@ //! Handles dynamic library loading for proc macro use crate::{proc_macro::bridge, rustc_server::TokenStream}; +use std::fs::File; use std::path::Path; use goblin::{mach::Mach, Object}; use libloading::Library; +use memmap::Mmap; use ra_proc_macro::ProcMacroKind; use std::io::Error as IoError; @@ -21,7 +23,8 @@ fn is_derive_registrar_symbol(symbol: &str) -> bool { } fn find_registrar_symbol(file: &Path) -> Result, IoError> { - let buffer = std::fs::read(file)?; + let file = File::open(file)?; + let buffer = unsafe { Mmap::map(&file)? }; let object = Object::parse(&buffer).map_err(invalid_data_err)?; match object { @@ -55,7 +58,7 @@ fn find_registrar_symbol(file: &Path) -> Result, IoError> { &s.name } }) - .find(|s| is_derive_registrar_symbol(&s)) + .find(|s| is_derive_registrar_symbol(s)) .map(|s| s.to_string()); Ok(name) } -- cgit v1.2.3 From 3b75bc154f75cb97ff2debee9f6594d6fc4f4053 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 17 Apr 2020 11:55:06 +0200 Subject: Better snippet when completing trait method --- crates/ra_ide/src/completion/complete_trait_impl.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index fab02945c..2ec0e7ce9 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs @@ -142,11 +142,11 @@ fn add_function_impl( CompletionItemKind::Function }; - let snippet = format!("{} {{}}", display); + let snippet = format!("{} {{\n $0\n}}", display); let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end()); - builder.text_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc); + builder.snippet_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc); } fn add_type_alias_impl( @@ -217,9 +217,10 @@ fn make_const_compl_syntax(const_: &ast::ConstDef) -> String { #[cfg(test)] mod tests { - use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; use insta::assert_debug_snapshot; + use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; + fn complete(code: &str) -> Vec { do_completion(code, CompletionKind::Magic) } @@ -255,7 +256,7 @@ mod tests { label: "fn test()", source_range: [209; 210), delete: [209; 210), - insert: "fn test() {}", + insert: "fn test() {\n $0\n}", kind: Function, lookup: "test", }, @@ -313,7 +314,7 @@ mod tests { label: "fn test()", source_range: [139; 140), delete: [139; 140), - insert: "fn test() {}", + insert: "fn test() {\n $0\n}", kind: Function, lookup: "test", }, @@ -342,7 +343,7 @@ mod tests { label: "fn foo()", source_range: [141; 142), delete: [138; 142), - insert: "fn foo() {}", + insert: "fn foo() {\n $0\n}", kind: Function, lookup: "foo", }, @@ -374,7 +375,7 @@ mod tests { label: "fn foo_bar()", source_range: [200; 201), delete: [197; 201), - insert: "fn foo_bar() {}", + insert: "fn foo_bar() {\n $0\n}", kind: Function, lookup: "foo_bar", }, @@ -425,7 +426,7 @@ mod tests { label: "fn foo()", source_range: [144; 145), delete: [141; 145), - insert: "fn foo() {}", + insert: "fn foo() {\n $0\n}", kind: Function, lookup: "foo", }, @@ -454,7 +455,7 @@ mod tests { label: "fn foo()", source_range: [166; 167), delete: [163; 167), - insert: "fn foo()\nwhere T: Into {}", + insert: "fn foo()\nwhere T: Into {\n $0\n}", kind: Function, lookup: "foo", }, -- cgit v1.2.3 From 302bf97bbf1855e3c7def9ab4f9f3d338be5e3b7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 17 Apr 2020 11:38:51 +0200 Subject: Don't expose impl details of SyntaxPtr --- crates/ra_hir_def/src/body.rs | 4 ++-- crates/ra_hir_def/src/body/lower.rs | 3 ++- crates/ra_hir_def/src/diagnostics.rs | 6 +++++- crates/ra_hir_def/src/nameres.rs | 3 ++- crates/ra_hir_expand/src/diagnostics.rs | 4 +--- crates/ra_hir_ty/src/diagnostics.rs | 24 +++++++++++++++++++++++- crates/ra_hir_ty/src/expr.rs | 14 +++++++++++--- crates/ra_hir_ty/src/infer.rs | 11 +++++++++-- 8 files changed, 55 insertions(+), 14 deletions(-) diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index eafaf48c1..3b169440a 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs @@ -210,7 +210,7 @@ pub struct BodySourceMap { expr_map_back: ArenaMap>, pat_map: FxHashMap, pat_map_back: ArenaMap>, - field_map: FxHashMap<(ExprId, usize), AstPtr>, + field_map: FxHashMap<(ExprId, usize), InFile>>, expansions: FxHashMap>, HirFileId>, } @@ -303,7 +303,7 @@ impl BodySourceMap { self.pat_map.get(&src).cloned() } - pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr { + pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile> { self.field_map[&(expr, field)].clone() } } diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index 79abe55ce..10a1ba714 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs @@ -320,7 +320,8 @@ impl ExprCollector<'_> { let res = self.alloc_expr(record_lit, syntax_ptr); for (i, ptr) in field_ptrs.into_iter().enumerate() { - self.source_map.field_map.insert((res, i), ptr); + let src = self.expander.to_source(ptr); + self.source_map.field_map.insert((res, i), src); } res } diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs index cfa0f2f76..dbaf4deef 100644 --- a/crates/ra_hir_def/src/diagnostics.rs +++ b/crates/ra_hir_def/src/diagnostics.rs @@ -4,7 +4,7 @@ use std::any::Any; use hir_expand::diagnostics::Diagnostic; use ra_db::RelativePathBuf; -use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; +use ra_syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; use hir_expand::{HirFileId, InFile}; @@ -12,6 +12,7 @@ use hir_expand::{HirFileId, InFile}; pub struct UnresolvedModule { pub file: HirFileId, pub decl: AstPtr, + pub highlight_range: TextRange, pub candidate: RelativePathBuf, } @@ -19,6 +20,9 @@ impl Diagnostic for UnresolvedModule { fn message(&self) -> String { "unresolved module".to_string() } + fn highlight_range(&self) -> TextRange { + self.highlight_range + } fn source(&self) -> InFile { InFile { file_id: self.file, value: self.decl.clone().into() } } diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index f279c2ad4..4a5a93dad 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs @@ -297,7 +297,7 @@ pub enum ModuleSource { mod diagnostics { use hir_expand::diagnostics::DiagnosticSink; use ra_db::RelativePathBuf; - use ra_syntax::{ast, AstPtr}; + use ra_syntax::{ast, AstNode, AstPtr}; use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId}; @@ -326,6 +326,7 @@ mod diagnostics { sink.push(UnresolvedModule { file: declaration.file_id, decl: AstPtr::new(&decl), + highlight_range: decl.syntax().text_range(), candidate: candidate.clone(), }) } diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs index 108c1e38c..714e700f7 100644 --- a/crates/ra_hir_expand/src/diagnostics.rs +++ b/crates/ra_hir_expand/src/diagnostics.rs @@ -22,10 +22,8 @@ use crate::{db::AstDatabase, InFile}; pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { fn message(&self) -> String; + fn highlight_range(&self) -> TextRange; fn source(&self) -> InFile; - fn highlight_range(&self) -> TextRange { - self.source().value.range() - } fn as_any(&self) -> &(dyn Any + Send + 'static); } diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index 927896d6f..da85bd082 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs @@ -3,7 +3,7 @@ use std::any::Any; use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; -use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; +use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr, TextRange}; use stdx::format_to; pub use hir_def::{diagnostics::UnresolvedModule, expr::MatchArm}; @@ -13,6 +13,7 @@ pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; pub struct NoSuchField { pub file: HirFileId, pub field: AstPtr, + pub highlight_range: TextRange, } impl Diagnostic for NoSuchField { @@ -20,6 +21,10 @@ impl Diagnostic for NoSuchField { "no such field".to_string() } + fn highlight_range(&self) -> TextRange { + self.highlight_range + } + fn source(&self) -> InFile { InFile { file_id: self.file, value: self.field.clone().into() } } @@ -33,6 +38,7 @@ impl Diagnostic for NoSuchField { pub struct MissingFields { pub file: HirFileId, pub field_list: AstPtr, + pub highlight_range: TextRange, pub missed_fields: Vec, } @@ -44,6 +50,10 @@ impl Diagnostic for MissingFields { } buf } + fn highlight_range(&self) -> TextRange { + self.highlight_range + } + fn source(&self) -> InFile { InFile { file_id: self.file, value: self.field_list.clone().into() } } @@ -66,6 +76,7 @@ impl AstDiagnostic for MissingFields { pub struct MissingPatFields { pub file: HirFileId, pub field_list: AstPtr, + pub highlight_range: TextRange, pub missed_fields: Vec, } @@ -77,6 +88,9 @@ impl Diagnostic for MissingPatFields { } buf } + fn highlight_range(&self) -> TextRange { + self.highlight_range + } fn source(&self) -> InFile { InFile { file_id: self.file, value: self.field_list.clone().into() } } @@ -90,12 +104,16 @@ pub struct MissingMatchArms { pub file: HirFileId, pub match_expr: AstPtr, pub arms: AstPtr, + pub highlight_range: TextRange, } impl Diagnostic for MissingMatchArms { fn message(&self) -> String { String::from("Missing match arm") } + fn highlight_range(&self) -> TextRange { + self.highlight_range + } fn source(&self) -> InFile { InFile { file_id: self.file, value: self.match_expr.clone().into() } } @@ -108,12 +126,16 @@ impl Diagnostic for MissingMatchArms { pub struct MissingOkInTailExpr { pub file: HirFileId, pub expr: AstPtr, + pub highlight_range: TextRange, } impl Diagnostic for MissingOkInTailExpr { fn message(&self) -> String { "wrap return expression in Ok".to_string() } + fn highlight_range(&self) -> TextRange { + self.highlight_range + } fn source(&self) -> InFile { InFile { file_id: self.file, value: self.expr.clone().into() } } diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs index fd59f4320..1d3950b70 100644 --- a/crates/ra_hir_ty/src/expr.rs +++ b/crates/ra_hir_ty/src/expr.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId}; use hir_expand::diagnostics::DiagnosticSink; -use ra_syntax::{ast, AstPtr}; +use ra_syntax::{ast, AstNode, AstPtr}; use rustc_hash::FxHashSet; use crate::{ @@ -100,6 +100,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { self.sink.push(MissingFields { file: source_ptr.file_id, field_list: AstPtr::new(&field_list), + highlight_range: field_list.syntax().text_range(), missed_fields, }) } @@ -130,6 +131,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { self.sink.push(MissingPatFields { file: source_ptr.file_id, field_list: AstPtr::new(&field_list), + highlight_range: field_list.syntax().text_range(), missed_fields, }) } @@ -213,6 +215,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { file: source_ptr.file_id, match_expr: AstPtr::new(&match_expr), arms: AstPtr::new(&arms), + highlight_range: match_expr.syntax().text_range(), }) } } @@ -244,8 +247,13 @@ impl<'a, 'b> ExprValidator<'a, 'b> { let (_, source_map) = db.body_with_source_map(self.func.into()); if let Ok(source_ptr) = source_map.expr_syntax(id) { - self.sink - .push(MissingOkInTailExpr { file: source_ptr.file_id, expr: source_ptr.value }); + let root = source_ptr.file_syntax(db.upcast()); + let highlight_range = source_ptr.value.to_node(&root).syntax().text_range(); + self.sink.push(MissingOkInTailExpr { + file: source_ptr.file_id, + expr: source_ptr.value, + highlight_range, + }); } } } diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 246b0e9be..7e6cdefe4 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -665,6 +665,7 @@ impl Expectation { mod diagnostics { use hir_def::{expr::ExprId, src::HasSource, FunctionId, Lookup}; use hir_expand::diagnostics::DiagnosticSink; + use ra_syntax::AstNode; use crate::{db::HirDatabase, diagnostics::NoSuchField}; @@ -682,10 +683,16 @@ mod diagnostics { ) { match self { InferenceDiagnostic::NoSuchField { expr, field } => { - let file = owner.lookup(db.upcast()).source(db.upcast()).file_id; + let source = owner.lookup(db.upcast()).source(db.upcast()); let (_, source_map) = db.body_with_source_map(owner.into()); let field = source_map.field_syntax(*expr, *field); - sink.push(NoSuchField { file, field }) + let root = field.file_syntax(db.upcast()); + let highlight_range = field.value.to_node(&root).syntax().text_range(); + sink.push(NoSuchField { + file: source.file_id, + field: field.value, + highlight_range, + }) } } } -- cgit v1.2.3 From a8196ffe8466aa60dec56e77c2da717793c0debe Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 17 Apr 2020 13:06:02 +0200 Subject: Correctly highlight ranges of diagnostics from macros closes #2799 --- crates/ra_hir/src/semantics.rs | 8 ++++ crates/ra_hir_def/src/diagnostics.rs | 6 +-- crates/ra_hir_expand/src/diagnostics.rs | 2 +- crates/ra_hir_ty/src/diagnostics.rs | 22 +++++----- crates/ra_ide/src/diagnostics.rs | 72 ++++++++++++++++++++++++++++++--- 5 files changed, 89 insertions(+), 21 deletions(-) diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 2707e422d..0b477f0e9 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs @@ -20,6 +20,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ db::HirDatabase, + diagnostics::Diagnostic, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{resolve_hir_path, SourceAnalyzer}, AssocItem, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, Name, @@ -126,6 +127,13 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { original_range(self.db, node.as_ref()) } + pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { + let src = diagnostics.source(); + let root = self.db.parse_or_expand(src.file_id).unwrap(); + let node = src.value.to_node(&root); + original_range(self.db, src.with_value(&node)) + } + pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator + '_ { let node = self.find_file(node); node.ancestors_with_macros(self.db).map(|it| it.value) diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs index dbaf4deef..2ee28fbaa 100644 --- a/crates/ra_hir_def/src/diagnostics.rs +++ b/crates/ra_hir_def/src/diagnostics.rs @@ -20,11 +20,11 @@ impl Diagnostic for UnresolvedModule { fn message(&self) -> String { "unresolved module".to_string() } - fn highlight_range(&self) -> TextRange { - self.highlight_range + fn highlight_range(&self) -> InFile { + InFile::new(self.file, self.highlight_range) } fn source(&self) -> InFile { - InFile { file_id: self.file, value: self.decl.clone().into() } + InFile::new(self.file, self.decl.clone().into()) } fn as_any(&self) -> &(dyn Any + Send + 'static) { self diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs index 714e700f7..813fbf0e2 100644 --- a/crates/ra_hir_expand/src/diagnostics.rs +++ b/crates/ra_hir_expand/src/diagnostics.rs @@ -22,7 +22,7 @@ use crate::{db::AstDatabase, InFile}; pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { fn message(&self) -> String; - fn highlight_range(&self) -> TextRange; + fn highlight_range(&self) -> InFile; fn source(&self) -> InFile; fn as_any(&self) -> &(dyn Any + Send + 'static); } diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index da85bd082..018c2ad3f 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs @@ -21,12 +21,12 @@ impl Diagnostic for NoSuchField { "no such field".to_string() } - fn highlight_range(&self) -> TextRange { - self.highlight_range + fn highlight_range(&self) -> InFile { + InFile::new(self.file, self.highlight_range) } fn source(&self) -> InFile { - InFile { file_id: self.file, value: self.field.clone().into() } + InFile::new(self.file, self.field.clone().into()) } fn as_any(&self) -> &(dyn Any + Send + 'static) { @@ -50,8 +50,8 @@ impl Diagnostic for MissingFields { } buf } - fn highlight_range(&self) -> TextRange { - self.highlight_range + fn highlight_range(&self) -> InFile { + InFile::new(self.file, self.highlight_range) } fn source(&self) -> InFile { @@ -88,8 +88,8 @@ impl Diagnostic for MissingPatFields { } buf } - fn highlight_range(&self) -> TextRange { - self.highlight_range + fn highlight_range(&self) -> InFile { + InFile::new(self.file, self.highlight_range) } fn source(&self) -> InFile { InFile { file_id: self.file, value: self.field_list.clone().into() } @@ -111,8 +111,8 @@ impl Diagnostic for MissingMatchArms { fn message(&self) -> String { String::from("Missing match arm") } - fn highlight_range(&self) -> TextRange { - self.highlight_range + fn highlight_range(&self) -> InFile { + InFile::new(self.file, self.highlight_range) } fn source(&self) -> InFile { InFile { file_id: self.file, value: self.match_expr.clone().into() } @@ -133,8 +133,8 @@ impl Diagnostic for MissingOkInTailExpr { fn message(&self) -> String { "wrap return expression in Ok".to_string() } - fn highlight_range(&self) -> TextRange { - self.highlight_range + fn highlight_range(&self) -> InFile { + InFile::new(self.file, self.highlight_range) } fn source(&self) -> InFile { InFile { file_id: self.file, value: self.expr.clone().into() } diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 901ad104c..e7e201709 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -1,4 +1,8 @@ -//! FIXME: write short doc here +//! Collects diagnostics & fixits for a single file. +//! +//! The tricky bit here is that diagnostics are produced by hir in terms of +//! macro-expanded files, but we need to present them to the users in terms of +//! original files. So we need to map the ranges. use std::cell::RefCell; @@ -46,7 +50,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec let mut sink = DiagnosticSink::new(|d| { res.borrow_mut().push(Diagnostic { message: d.message(), - range: d.highlight_range(), + range: sema.diagnostics_range(d).range, severity: Severity::Error, fix: None, }) @@ -62,7 +66,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec let create_file = FileSystemEdit::CreateFile { source_root, path }; let fix = SourceChange::file_system_edit("create module", create_file); res.borrow_mut().push(Diagnostic { - range: d.highlight_range(), + range: sema.diagnostics_range(d).range, message: d.message(), severity: Severity::Error, fix: Some(fix), @@ -95,7 +99,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec }; res.borrow_mut().push(Diagnostic { - range: d.highlight_range(), + range: sema.diagnostics_range(d).range, message: d.message(), severity: Severity::Error, fix, @@ -103,7 +107,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec }) .on::(|d| { res.borrow_mut().push(Diagnostic { - range: d.highlight_range(), + range: sema.diagnostics_range(d).range, message: d.message(), severity: Severity::Error, fix: None, @@ -115,7 +119,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec let edit = TextEdit::replace(node.syntax().text_range(), replacement); let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit); res.borrow_mut().push(Diagnostic { - range: d.highlight_range(), + range: sema.diagnostics_range(d).range, message: d.message(), severity: Severity::Error, fix: Some(fix), @@ -621,6 +625,62 @@ mod tests { "###); } + #[test] + fn range_mapping_out_of_macros() { + let (analysis, file_id) = single_file( + r" + fn some() {} + fn items() {} + fn here() {} + + macro_rules! id { + ($($tt:tt)*) => { $($tt)*}; + } + + fn main() { + let _x = id![Foo { a: 42 }]; + } + + pub struct Foo { + pub a: i32, + pub b: i32, + } + ", + ); + let diagnostics = analysis.diagnostics(file_id).unwrap(); + assert_debug_snapshot!(diagnostics, @r###" + [ + Diagnostic { + message: "Missing structure fields:\n- b", + range: [224; 233), + fix: Some( + SourceChange { + label: "fill struct fields", + source_file_edits: [ + SourceFileEdit { + file_id: FileId( + 1, + ), + edit: TextEdit { + atoms: [ + AtomTextEdit { + delete: [3; 9), + insert: "{a:42, b: ()}", + }, + ], + }, + }, + ], + file_system_edits: [], + cursor_position: None, + }, + ), + severity: Error, + }, + ] + "###); + } + #[test] fn test_check_unnecessary_braces_in_use_statement() { check_not_applicable( -- cgit v1.2.3 From 146f6f5a45a4bfd98ab0eb54bb30610d784433c9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 17 Apr 2020 13:55:05 +0200 Subject: Simplify Diagnostic structure It's not entirely clear what subnode ranges should mean in the presence of macros, so let's leave them out for now. We are not using them heavily anyway. --- crates/ra_hir_def/src/diagnostics.rs | 6 +----- crates/ra_hir_def/src/nameres.rs | 3 +-- crates/ra_hir_expand/src/diagnostics.rs | 3 +-- crates/ra_hir_ty/src/diagnostics.rs | 24 +----------------------- crates/ra_hir_ty/src/expr.rs | 14 +++----------- crates/ra_hir_ty/src/infer.rs | 9 +-------- 6 files changed, 8 insertions(+), 51 deletions(-) diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs index 2ee28fbaa..510c5e064 100644 --- a/crates/ra_hir_def/src/diagnostics.rs +++ b/crates/ra_hir_def/src/diagnostics.rs @@ -4,7 +4,7 @@ use std::any::Any; use hir_expand::diagnostics::Diagnostic; use ra_db::RelativePathBuf; -use ra_syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; +use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; use hir_expand::{HirFileId, InFile}; @@ -12,7 +12,6 @@ use hir_expand::{HirFileId, InFile}; pub struct UnresolvedModule { pub file: HirFileId, pub decl: AstPtr, - pub highlight_range: TextRange, pub candidate: RelativePathBuf, } @@ -20,9 +19,6 @@ impl Diagnostic for UnresolvedModule { fn message(&self) -> String { "unresolved module".to_string() } - fn highlight_range(&self) -> InFile { - InFile::new(self.file, self.highlight_range) - } fn source(&self) -> InFile { InFile::new(self.file, self.decl.clone().into()) } diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index 4a5a93dad..f279c2ad4 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs @@ -297,7 +297,7 @@ pub enum ModuleSource { mod diagnostics { use hir_expand::diagnostics::DiagnosticSink; use ra_db::RelativePathBuf; - use ra_syntax::{ast, AstNode, AstPtr}; + use ra_syntax::{ast, AstPtr}; use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId}; @@ -326,7 +326,6 @@ mod diagnostics { sink.push(UnresolvedModule { file: declaration.file_id, decl: AstPtr::new(&decl), - highlight_range: decl.syntax().text_range(), candidate: candidate.clone(), }) } diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs index 813fbf0e2..99209c6e8 100644 --- a/crates/ra_hir_expand/src/diagnostics.rs +++ b/crates/ra_hir_expand/src/diagnostics.rs @@ -16,13 +16,12 @@ use std::{any::Any, fmt}; -use ra_syntax::{SyntaxNode, SyntaxNodePtr, TextRange}; +use ra_syntax::{SyntaxNode, SyntaxNodePtr}; use crate::{db::AstDatabase, InFile}; pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { fn message(&self) -> String; - fn highlight_range(&self) -> InFile; fn source(&self) -> InFile; fn as_any(&self) -> &(dyn Any + Send + 'static); } diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index 018c2ad3f..c8fd54861 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs @@ -3,7 +3,7 @@ use std::any::Any; use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; -use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr, TextRange}; +use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; use stdx::format_to; pub use hir_def::{diagnostics::UnresolvedModule, expr::MatchArm}; @@ -13,7 +13,6 @@ pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; pub struct NoSuchField { pub file: HirFileId, pub field: AstPtr, - pub highlight_range: TextRange, } impl Diagnostic for NoSuchField { @@ -21,10 +20,6 @@ impl Diagnostic for NoSuchField { "no such field".to_string() } - fn highlight_range(&self) -> InFile { - InFile::new(self.file, self.highlight_range) - } - fn source(&self) -> InFile { InFile::new(self.file, self.field.clone().into()) } @@ -38,7 +33,6 @@ impl Diagnostic for NoSuchField { pub struct MissingFields { pub file: HirFileId, pub field_list: AstPtr, - pub highlight_range: TextRange, pub missed_fields: Vec, } @@ -50,10 +44,6 @@ impl Diagnostic for MissingFields { } buf } - fn highlight_range(&self) -> InFile { - InFile::new(self.file, self.highlight_range) - } - fn source(&self) -> InFile { InFile { file_id: self.file, value: self.field_list.clone().into() } } @@ -76,7 +66,6 @@ impl AstDiagnostic for MissingFields { pub struct MissingPatFields { pub file: HirFileId, pub field_list: AstPtr, - pub highlight_range: TextRange, pub missed_fields: Vec, } @@ -88,9 +77,6 @@ impl Diagnostic for MissingPatFields { } buf } - fn highlight_range(&self) -> InFile { - InFile::new(self.file, self.highlight_range) - } fn source(&self) -> InFile { InFile { file_id: self.file, value: self.field_list.clone().into() } } @@ -104,16 +90,12 @@ pub struct MissingMatchArms { pub file: HirFileId, pub match_expr: AstPtr, pub arms: AstPtr, - pub highlight_range: TextRange, } impl Diagnostic for MissingMatchArms { fn message(&self) -> String { String::from("Missing match arm") } - fn highlight_range(&self) -> InFile { - InFile::new(self.file, self.highlight_range) - } fn source(&self) -> InFile { InFile { file_id: self.file, value: self.match_expr.clone().into() } } @@ -126,16 +108,12 @@ impl Diagnostic for MissingMatchArms { pub struct MissingOkInTailExpr { pub file: HirFileId, pub expr: AstPtr, - pub highlight_range: TextRange, } impl Diagnostic for MissingOkInTailExpr { fn message(&self) -> String { "wrap return expression in Ok".to_string() } - fn highlight_range(&self) -> InFile { - InFile::new(self.file, self.highlight_range) - } fn source(&self) -> InFile { InFile { file_id: self.file, value: self.expr.clone().into() } } diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs index 1d3950b70..fd59f4320 100644 --- a/crates/ra_hir_ty/src/expr.rs +++ b/crates/ra_hir_ty/src/expr.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId}; use hir_expand::diagnostics::DiagnosticSink; -use ra_syntax::{ast, AstNode, AstPtr}; +use ra_syntax::{ast, AstPtr}; use rustc_hash::FxHashSet; use crate::{ @@ -100,7 +100,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> { self.sink.push(MissingFields { file: source_ptr.file_id, field_list: AstPtr::new(&field_list), - highlight_range: field_list.syntax().text_range(), missed_fields, }) } @@ -131,7 +130,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> { self.sink.push(MissingPatFields { file: source_ptr.file_id, field_list: AstPtr::new(&field_list), - highlight_range: field_list.syntax().text_range(), missed_fields, }) } @@ -215,7 +213,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> { file: source_ptr.file_id, match_expr: AstPtr::new(&match_expr), arms: AstPtr::new(&arms), - highlight_range: match_expr.syntax().text_range(), }) } } @@ -247,13 +244,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { let (_, source_map) = db.body_with_source_map(self.func.into()); if let Ok(source_ptr) = source_map.expr_syntax(id) { - let root = source_ptr.file_syntax(db.upcast()); - let highlight_range = source_ptr.value.to_node(&root).syntax().text_range(); - self.sink.push(MissingOkInTailExpr { - file: source_ptr.file_id, - expr: source_ptr.value, - highlight_range, - }); + self.sink + .push(MissingOkInTailExpr { file: source_ptr.file_id, expr: source_ptr.value }); } } } diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 7e6cdefe4..b6d9b3438 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -665,7 +665,6 @@ impl Expectation { mod diagnostics { use hir_def::{expr::ExprId, src::HasSource, FunctionId, Lookup}; use hir_expand::diagnostics::DiagnosticSink; - use ra_syntax::AstNode; use crate::{db::HirDatabase, diagnostics::NoSuchField}; @@ -686,13 +685,7 @@ mod diagnostics { let source = owner.lookup(db.upcast()).source(db.upcast()); let (_, source_map) = db.body_with_source_map(owner.into()); let field = source_map.field_syntax(*expr, *field); - let root = field.file_syntax(db.upcast()); - let highlight_range = field.value.to_node(&root).syntax().text_range(); - sink.push(NoSuchField { - file: source.file_id, - field: field.value, - highlight_range, - }) + sink.push(NoSuchField { file: source.file_id, field: field.value }) } } } -- cgit v1.2.3 From 028f1e2e3add764956911a0f2663107cb945c0ec Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 17 Apr 2020 14:28:20 +0200 Subject: Don\t suggest import itself as a completion for import --- .../src/completion/complete_unqualified_path.rs | 40 +++++++++++++++++++++- crates/ra_ide/src/marks.rs | 1 + 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index 2d8e0776c..ad5fdcc4e 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs @@ -1,6 +1,10 @@ //! Completion of names from the current scope, e.g. locals and imported items. +use hir::ScopeDef; +use test_utils::tested_by; + use crate::completion::{CompletionContext, Completions}; +use ra_syntax::AstNode; pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { if !ctx.is_trivial_path { @@ -14,12 +18,23 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC return; } - ctx.scope().process_all_names(&mut |name, res| acc.add_resolution(ctx, name.to_string(), &res)); + ctx.scope().process_all_names(&mut |name, res| { + if ctx.use_item_syntax.is_some() { + if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { + if name_ref.syntax().text() == name.to_string().as_str() { + tested_by!(self_fulfilling_completion); + return; + } + } + } + acc.add_resolution(ctx, name.to_string(), &res) + }); } #[cfg(test)] mod tests { use insta::assert_debug_snapshot; + use test_utils::covers; use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; @@ -27,6 +42,29 @@ mod tests { do_completion(ra_fixture, CompletionKind::Reference) } + #[test] + fn self_fulfilling_completion() { + covers!(self_fulfilling_completion); + assert_debug_snapshot!( + do_reference_completion( + r#" + use foo<|> + use std::collections; + "#, + ), + @r###" + [ + CompletionItem { + label: "collections", + source_range: [21; 24), + delete: [21; 24), + insert: "collections", + }, + ] + "### + ); + } + #[test] fn bind_pat_and_path_ignore_at() { assert_debug_snapshot!( diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs index 5e1f135c5..eee44e886 100644 --- a/crates/ra_ide/src/marks.rs +++ b/crates/ra_ide/src/marks.rs @@ -8,4 +8,5 @@ test_utils::marks!( test_resolve_parent_module_on_module_decl search_filters_by_range dont_insert_macro_call_parens_unncessary + self_fulfilling_completion ); -- cgit v1.2.3 From 408f914bf4d6719ae68582ae43e2de9d3cb362b0 Mon Sep 17 00:00:00 2001 From: Josh Mcguigan Date: Fri, 17 Apr 2020 05:36:44 -0700 Subject: fix panic on ellipsis in pattern --- crates/ra_hir_def/src/body/lower.rs | 13 +++++++-- crates/ra_hir_ty/src/tests/regression.rs | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index 79abe55ce..0679ffa1e 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs @@ -650,6 +650,7 @@ impl ExprCollector<'_> { ast::Pat::SlicePat(p) => { let SlicePatComponents { prefix, slice, suffix } = p.components(); + // FIXME properly handle `DotDotPat` Pat::Slice { prefix: prefix.into_iter().map(|p| self.collect_pat(p)).collect(), slice: slice.map(|p| self.collect_pat(p)), @@ -666,9 +667,15 @@ impl ExprCollector<'_> { Pat::Missing } } - ast::Pat::DotDotPat(_) => unreachable!( - "`DotDotPat` requires special handling and should not be mapped to a Pat." - ), + ast::Pat::DotDotPat(_) => { + // `DotDotPat` requires special handling and should not be mapped + // to a Pat. Here we are using `Pat::Missing` as a fallback for + // when `DotDotPat` is mapped to `Pat`, which can easily happen + // when the source code being analyzed has a malformed pattern + // which includes `..` in a place where it isn't valid. + + Pat::Missing + } // FIXME: implement ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing, }; diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs index d69115a2f..61284d672 100644 --- a/crates/ra_hir_ty/src/tests/regression.rs +++ b/crates/ra_hir_ty/src/tests/regression.rs @@ -484,3 +484,52 @@ fn main() { assert_eq!("()", super::type_at_pos(&db, pos)); } + +#[test] +fn issue_3999_slice() { + assert_snapshot!( + infer(r#" +fn foo(params: &[usize]) { + match params { + [ps @ .., _] => {} + } +} +"#), + @r###" + [8; 14) 'params': &[usize] + [26; 81) '{ ... } }': () + [32; 79) 'match ... }': () + [38; 44) 'params': &[usize] + [55; 67) '[ps @ .., _]': [usize] + [65; 66) '_': usize + [71; 73) '{}': () + "### + ); +} + +#[test] +fn issue_3999_struct() { + // rust-analyzer should not panic on seeing this malformed + // record pattern. + assert_snapshot!( + infer(r#" +struct Bar { + a: bool, +} +fn foo(b: Bar) { + match b { + Bar { a: .. } => {}, + } +} +"#), + @r###" + [36; 37) 'b': Bar + [44; 96) '{ ... } }': () + [50; 94) 'match ... }': () + [56; 57) 'b': Bar + [68; 81) 'Bar { a: .. }': Bar + [77; 79) '..': bool + [85; 87) '{}': () + "### + ); +} -- cgit v1.2.3 From f178df1a5e23d584f84658a4af426b8aeb832ea4 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 17 Apr 2020 18:04:49 +0200 Subject: Don't use SyntaxNodePtr::range when determining scope for offset --- crates/ra_hir/src/source_analyzer.rs | 99 +++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index 58ae6ce41..23af400b8 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs @@ -23,7 +23,7 @@ use hir_ty::{ }; use ra_syntax::{ ast::{self, AstNode}, - SyntaxNode, SyntaxNodePtr, TextUnit, + SyntaxNode, TextRange, TextUnit, }; use crate::{ @@ -56,7 +56,7 @@ impl SourceAnalyzer { let scopes = db.expr_scopes(def); let scope = match offset { None => scope_for(&scopes, &source_map, node), - Some(offset) => scope_for_offset(&scopes, &source_map, node.with_value(offset)), + Some(offset) => scope_for_offset(db, &scopes, &source_map, node.with_value(offset)), }; let resolver = resolver_for_scope(db.upcast(), def, scope); SourceAnalyzer { @@ -304,6 +304,7 @@ fn scope_for( } fn scope_for_offset( + db: &dyn HirDatabase, scopes: &ExprScopes, source_map: &BodySourceMap, offset: InFile, @@ -317,21 +318,63 @@ fn scope_for_offset( if source.file_id != offset.file_id { return None; } - let syntax_node_ptr = source.value.syntax_node_ptr(); - Some((syntax_node_ptr, scope)) + let root = source.file_syntax(db.upcast()); + let node = source.value.to_node(&root); + Some((node.syntax().text_range(), scope)) }) // find containing scope - .min_by_key(|(ptr, _scope)| { + .min_by_key(|(expr_range, _scope)| { ( - !(ptr.range().start() <= offset.value && offset.value <= ptr.range().end()), - ptr.range().len(), + !(expr_range.start() <= offset.value && offset.value <= expr_range.end()), + expr_range.len(), ) }) - .map(|(ptr, scope)| { - adjust(scopes, source_map, ptr, offset.file_id, offset.value).unwrap_or(*scope) + .map(|(expr_range, scope)| { + adjust(db, scopes, source_map, expr_range, offset.file_id, offset.value) + .unwrap_or(*scope) }) } +// XXX: during completion, cursor might be outside of any particular +// expression. Try to figure out the correct scope... +fn adjust( + db: &dyn HirDatabase, + scopes: &ExprScopes, + source_map: &BodySourceMap, + expr_range: TextRange, + file_id: HirFileId, + offset: TextUnit, +) -> Option { + let child_scopes = scopes + .scope_by_expr() + .iter() + .filter_map(|(id, scope)| { + let source = source_map.expr_syntax(*id).ok()?; + // FIXME: correctly handle macro expansion + if source.file_id != file_id { + return None; + } + let root = source.file_syntax(db.upcast()); + let node = source.value.to_node(&root); + Some((node.syntax().text_range(), scope)) + }) + .filter(|(range, _)| { + range.start() <= offset && range.is_subrange(&expr_range) && *range != expr_range + }); + + child_scopes + .max_by(|(r1, _), (r2, _)| { + if r2.is_subrange(&r1) { + std::cmp::Ordering::Greater + } else if r1.is_subrange(&r2) { + std::cmp::Ordering::Less + } else { + r1.start().cmp(&r2.start()) + } + }) + .map(|(_ptr, scope)| *scope) +} + pub(crate) fn resolve_hir_path( db: &dyn HirDatabase, resolver: &Resolver, @@ -376,41 +419,3 @@ pub(crate) fn resolve_hir_path( .map(|def| PathResolution::Macro(def.into())) }) } - -// XXX: during completion, cursor might be outside of any particular -// expression. Try to figure out the correct scope... -fn adjust( - scopes: &ExprScopes, - source_map: &BodySourceMap, - ptr: SyntaxNodePtr, - file_id: HirFileId, - offset: TextUnit, -) -> Option { - let r = ptr.range(); - let child_scopes = scopes - .scope_by_expr() - .iter() - .filter_map(|(id, scope)| { - let source = source_map.expr_syntax(*id).ok()?; - // FIXME: correctly handle macro expansion - if source.file_id != file_id { - return None; - } - let syntax_node_ptr = source.value.syntax_node_ptr(); - Some((syntax_node_ptr, scope)) - }) - .map(|(ptr, scope)| (ptr.range(), scope)) - .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); - - child_scopes - .max_by(|(r1, _), (r2, _)| { - if r2.is_subrange(&r1) { - std::cmp::Ordering::Greater - } else if r1.is_subrange(&r2) { - std::cmp::Ordering::Less - } else { - r1.start().cmp(&r2.start()) - } - }) - .map(|(_ptr, scope)| *scope) -} -- cgit v1.2.3 From fbd95785a6746a8bf100a0417b592f5fa35df882 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 17 Apr 2020 18:27:20 +0200 Subject: Add two more tests for associated types --- crates/ra_hir_ty/src/tests/traits.rs | 174 +++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 0a889f805..dc517fc4a 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -2204,3 +2204,177 @@ fn test(x: Box) { ); assert_eq!(t, "()"); } + +#[test] +fn string_to_owned() { + let t = type_at( + r#" +//- /main.rs +struct String {} +pub trait ToOwned { + type Owned; + fn to_owned(&self) -> Self::Owned; +} +impl ToOwned for str { + type Owned = String; +} +fn test() { + "foo".to_owned()<|>; +} +"#, + ); + assert_eq!(t, "String"); +} + +#[test] +fn iterator_chain() { + assert_snapshot!( + infer(r#" +//- /main.rs +#[lang = "fn_once"] +trait FnOnce { + type Output; +} +#[lang = "fn_mut"] +trait FnMut: FnOnce { } + +enum Option { Some(T), None } +use Option::*; + +pub trait Iterator { + type Item; + + fn filter_map(self, f: F) -> FilterMap + where + F: FnMut(Self::Item) -> Option, + { loop {} } + + fn for_each(self, f: F) + where + F: FnMut(Self::Item), + { loop {} } +} + +pub trait IntoIterator { + type Item; + type IntoIter: Iterator; + fn into_iter(self) -> Self::IntoIter; +} + +pub struct FilterMap { } +impl Iterator for FilterMap +where + F: FnMut(I::Item) -> Option, +{ + type Item = B; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for I { + type Item = I::Item; + type IntoIter = I; + + fn into_iter(self) -> I { + self + } +} + +struct Vec {} +impl Vec { + fn new() -> Self { loop {} } +} + +impl IntoIterator for Vec { + type Item = T; + type IntoIter = IntoIter; +} + +pub struct IntoIter { } +impl Iterator for IntoIter { + type Item = T; +} + +fn main() { + Vec::::new().into_iter() + .filter_map(|x| if x > 0 { Some(x as u32) } else { None }) + .for_each(|y| { y; }); +} +"#), + @r###" + [240; 244) 'self': Self + [246; 247) 'f': F + [331; 342) '{ loop {} }': FilterMap + [333; 340) 'loop {}': ! + [338; 340) '{}': () + [363; 367) 'self': Self + [369; 370) 'f': F + [419; 430) '{ loop {} }': () + [421; 428) 'loop {}': ! + [426; 428) '{}': () + [539; 543) 'self': Self + [868; 872) 'self': I + [879; 899) '{ ... }': I + [889; 893) 'self': I + [958; 969) '{ loop {} }': Vec + [960; 967) 'loop {}': ! + [965; 967) '{}': () + [1156; 1287) '{ ... }); }': () + [1162; 1177) 'Vec::::new': fn new() -> Vec + [1162; 1179) 'Vec::<...:new()': Vec + [1162; 1191) 'Vec::<...iter()': IntoIter + [1162; 1256) 'Vec::<...one })': FilterMap, |i32| -> Option> + [1162; 1284) 'Vec::<... y; })': () + [1210; 1255) '|x| if...None }': |i32| -> Option + [1211; 1212) 'x': i32 + [1214; 1255) 'if x >...None }': Option + [1217; 1218) 'x': i32 + [1217; 1222) 'x > 0': bool + [1221; 1222) '0': i32 + [1223; 1241) '{ Some...u32) }': Option + [1225; 1229) 'Some': Some(u32) -> Option + [1225; 1239) 'Some(x as u32)': Option + [1230; 1231) 'x': i32 + [1230; 1238) 'x as u32': u32 + [1247; 1255) '{ None }': Option + [1249; 1253) 'None': Option + [1273; 1283) '|y| { y; }': |u32| -> () + [1274; 1275) 'y': u32 + [1277; 1283) '{ y; }': () + [1279; 1280) 'y': u32 + "### + ); +} + +#[test] +fn nested_assoc() { + let t = type_at( + r#" +//- /main.rs +struct Bar; +struct Foo; + +trait A { + type OutputA; +} + +impl A for Bar { + type OutputA = Foo; +} + +trait B { + type Output; + fn foo() -> Self::Output; +} + +impl B for T { + type Output = T::OutputA; + fn foo() -> Self::Output { loop {} } +} + +fn main() { + Bar::foo()<|>; +} +"#, + ); + assert_eq!(t, "Foo"); +} -- cgit v1.2.3 From 6a7fc76b89dca4d1b4e3e50047183535aee98627 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 17 Apr 2020 19:41:37 +0200 Subject: Fix type equality for dyn Trait Fixes a lot of false type mismatches. (And as always when touching the unification code, I have to say I'm looking forward to replacing it by Chalk's...) --- crates/ra_hir_ty/src/infer/coerce.rs | 4 ++-- crates/ra_hir_ty/src/infer/unify.rs | 42 +++++++++++++++++++++++++++++++++--- crates/ra_hir_ty/src/tests/traits.rs | 24 +++++++++++++++++++++ 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 959b1e212..89200255a 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs @@ -51,7 +51,7 @@ impl<'a> InferenceContext<'a> { // Trivial cases, this should go after `never` check to // avoid infer result type to be never _ => { - if self.table.unify_inner_trivial(&from_ty, &to_ty) { + if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) { return true; } } @@ -175,7 +175,7 @@ impl<'a> InferenceContext<'a> { return self.table.unify_substs(st1, st2, 0); } _ => { - if self.table.unify_inner_trivial(&derefed_ty, &to_ty) { + if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) { return true; } } diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs index 5f6cea8d3..ab0bc8b70 100644 --- a/crates/ra_hir_ty/src/infer/unify.rs +++ b/crates/ra_hir_ty/src/infer/unify.rs @@ -8,7 +8,8 @@ use test_utils::tested_by; use super::{InferenceContext, Obligation}; use crate::{ - BoundVar, Canonical, DebruijnIndex, InEnvironment, InferTy, Substs, Ty, TypeCtor, TypeWalk, + BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferTy, Substs, Ty, + TypeCtor, TypeWalk, }; impl<'a> InferenceContext<'a> { @@ -226,16 +227,26 @@ impl InferenceTable { (Ty::Apply(a_ty1), Ty::Apply(a_ty2)) if a_ty1.ctor == a_ty2.ctor => { self.unify_substs(&a_ty1.parameters, &a_ty2.parameters, depth + 1) } - _ => self.unify_inner_trivial(&ty1, &ty2), + + _ => self.unify_inner_trivial(&ty1, &ty2, depth), } } - pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty) -> bool { + pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool { match (ty1, ty2) { (Ty::Unknown, _) | (_, Ty::Unknown) => true, (Ty::Placeholder(p1), Ty::Placeholder(p2)) if *p1 == *p2 => true, + (Ty::Dyn(dyn1), Ty::Dyn(dyn2)) if dyn1.len() == dyn2.len() => { + for (pred1, pred2) in dyn1.iter().zip(dyn2.iter()) { + if !self.unify_preds(pred1, pred2, depth + 1) { + return false; + } + } + true + } + (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2))) | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) @@ -268,6 +279,31 @@ impl InferenceTable { } } + fn unify_preds( + &mut self, + pred1: &GenericPredicate, + pred2: &GenericPredicate, + depth: usize, + ) -> bool { + match (pred1, pred2) { + (GenericPredicate::Implemented(tr1), GenericPredicate::Implemented(tr2)) + if tr1.trait_ == tr2.trait_ => + { + self.unify_substs(&tr1.substs, &tr2.substs, depth + 1) + } + (GenericPredicate::Projection(proj1), GenericPredicate::Projection(proj2)) + if proj1.projection_ty.associated_ty == proj2.projection_ty.associated_ty => + { + self.unify_substs( + &proj1.projection_ty.parameters, + &proj2.projection_ty.parameters, + depth + 1, + ) && self.unify_inner(&proj1.ty, &proj2.ty, depth + 1) + } + _ => false, + } + } + /// If `ty` is a type variable with known type, returns that type; /// otherwise, return ty. pub fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index dc517fc4a..f6e3e07cd 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -2378,3 +2378,27 @@ fn main() { ); assert_eq!(t, "Foo"); } + +#[test] +fn trait_object_no_coercion() { + assert_snapshot!( + infer_with_mismatches(r#" +trait Foo {} + +fn foo(x: &dyn Foo) {} + +fn test(x: &dyn Foo) { + foo(x); +} +"#, true), + @r###" + [22; 23) 'x': &dyn Foo + [35; 37) '{}': () + [47; 48) 'x': &dyn Foo + [60; 75) '{ foo(x); }': () + [66; 69) 'foo': fn foo(&dyn Foo) + [66; 72) 'foo(x)': () + [70; 71) 'x': &dyn Foo + "### + ); +} -- cgit v1.2.3 From 2e2c03ee2d7ef20fd6e20151507d26a4407c835e Mon Sep 17 00:00:00 2001 From: Leander Tentrup Date: Fri, 17 Apr 2020 22:23:23 +0200 Subject: Fix incorrect order of syntax highlight ranges --- crates/ra_ide/src/syntax_highlighting.rs | 3 ++- crates/ra_ide/src/syntax_highlighting/tests.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 83d161f45..7b15b82bd 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -174,7 +174,8 @@ pub(crate) fn highlight( } assert_eq!(res.len(), 1, "after DFS traversal, the stack should only contain a single element"); - let res = res.pop().unwrap(); + let mut res = res.pop().unwrap(); + res.sort_by_key(|range| range.range.start()); // Check that ranges are sorted and disjoint assert!(res .iter() diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index 110887c2a..73611e23a 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs @@ -156,3 +156,15 @@ fn main() { fs::write(dst_file, &actual_html).unwrap(); assert_eq_text!(expected_html, actual_html); } + +#[test] +fn ranges_sorted() { + let (analysis, file_id) = single_file( + r#" +#[foo(bar = "bar")] +macro_rules! test {} +}"# + .trim(), + ); + let _ = analysis.highlight(file_id).unwrap(); +} -- cgit v1.2.3 From d3cb9ea0bfb6c9e6c8b57a46feb1de696084d994 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 17 Apr 2020 22:48:29 +0200 Subject: Fix another crash from wrong binders Basically, if we had something like `dyn Trait` (where `T` is a type parameter) in an impl we lowered that to `dyn Trait<^0.0>`, when it should be `dyn Trait<^1.0>` because the `dyn` introduces a new binder. With one type parameter, that's just wrong, with two, it'll lead to crashes. --- crates/ra_hir_ty/src/lib.rs | 4 +-- crates/ra_hir_ty/src/lower.rs | 69 +++++++++++++++++++++++++----------- crates/ra_hir_ty/src/tests/traits.rs | 36 +++++++++++++++++++ crates/ra_hir_ty/src/traits/chalk.rs | 10 +++--- 4 files changed, 92 insertions(+), 27 deletions(-) diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 2677f3af2..a4b8d6683 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -396,12 +396,12 @@ impl Substs { } /// Return Substs that replace each parameter by a bound variable. - pub(crate) fn bound_vars(generic_params: &Generics) -> Substs { + pub(crate) fn bound_vars(generic_params: &Generics, debruijn: DebruijnIndex) -> Substs { Substs( generic_params .iter() .enumerate() - .map(|(idx, _)| Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, idx))) + .map(|(idx, _)| Ty::Bound(BoundVar::new(debruijn, idx))) .collect(), ) } diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index cc1ac8e3e..c2812e178 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -39,6 +39,7 @@ use crate::{ pub struct TyLoweringContext<'a> { pub db: &'a dyn HirDatabase, pub resolver: &'a Resolver, + in_binders: DebruijnIndex, /// Note: Conceptually, it's thinkable that we could be in a location where /// some type params should be represented as placeholders, and others /// should be converted to variables. I think in practice, this isn't @@ -53,7 +54,27 @@ impl<'a> TyLoweringContext<'a> { let impl_trait_counter = std::cell::Cell::new(0); let impl_trait_mode = ImplTraitLoweringMode::Disallowed; let type_param_mode = TypeParamLoweringMode::Placeholder; - Self { db, resolver, impl_trait_mode, impl_trait_counter, type_param_mode } + let in_binders = DebruijnIndex::INNERMOST; + Self { db, resolver, in_binders, impl_trait_mode, impl_trait_counter, type_param_mode } + } + + pub fn with_shifted_in( + &self, + debruijn: DebruijnIndex, + f: impl FnOnce(&TyLoweringContext) -> T, + ) -> T { + let new_ctx = Self { + in_binders: self.in_binders.shifted_in_from(debruijn), + impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), + ..*self + }; + let result = f(&new_ctx); + self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); + result + } + + pub fn shifted_in(self, debruijn: DebruijnIndex) -> Self { + Self { in_binders: self.in_binders.shifted_in_from(debruijn), ..self } } pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { @@ -134,22 +155,26 @@ impl Ty { } TypeRef::DynTrait(bounds) => { let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); - let predicates = bounds - .iter() - .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone())) - .collect(); + let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { + bounds + .iter() + .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone())) + .collect() + }); Ty::Dyn(predicates) } TypeRef::ImplTrait(bounds) => { match ctx.impl_trait_mode { ImplTraitLoweringMode::Opaque => { let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); - let predicates = bounds - .iter() - .flat_map(|b| { - GenericPredicate::from_type_bound(ctx, b, self_ty.clone()) - }) - .collect(); + let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { + bounds + .iter() + .flat_map(|b| { + GenericPredicate::from_type_bound(ctx, b, self_ty.clone()) + }) + .collect() + }); Ty::Opaque(predicates) } ImplTraitLoweringMode::Param => { @@ -180,7 +205,7 @@ impl Ty { (0, 0, 0, 0) }; Ty::Bound(BoundVar::new( - DebruijnIndex::INNERMOST, + ctx.in_binders, idx as usize + parent_params + self_params + list_params, )) } @@ -293,7 +318,7 @@ impl Ty { TypeParamLoweringMode::Placeholder => Ty::Placeholder(param_id), TypeParamLoweringMode::Variable => { let idx = generics.param_idx(param_id).expect("matching generics"); - Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, idx)) + Ty::Bound(BoundVar::new(ctx.in_binders, idx)) } } } @@ -303,7 +328,9 @@ impl Ty { TypeParamLoweringMode::Placeholder => { Substs::type_params_for_generics(&generics) } - TypeParamLoweringMode::Variable => Substs::bound_vars(&generics), + TypeParamLoweringMode::Variable => { + Substs::bound_vars(&generics, ctx.in_binders) + } }; ctx.db.impl_self_ty(impl_id).subst(&substs) } @@ -313,7 +340,9 @@ impl Ty { TypeParamLoweringMode::Placeholder => { Substs::type_params_for_generics(&generics) } - TypeParamLoweringMode::Variable => Substs::bound_vars(&generics), + TypeParamLoweringMode::Variable => { + Substs::bound_vars(&generics, ctx.in_binders) + } }; ctx.db.ty(adt.into()).subst(&substs) } @@ -797,7 +826,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { /// function body. fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders { let generics = generics(db.upcast(), def.into()); - let substs = Substs::bound_vars(&generics); + let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs)) } @@ -851,7 +880,7 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders Binders { let generics = generics(db.upcast(), adt.into()); - let substs = Substs::bound_vars(&generics); + let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); Binders::new(substs.len(), Ty::apply(TypeCtor::Adt(adt), substs)) } @@ -892,7 +921,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders { let ctx = TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); let type_ref = &db.type_alias_data(t).type_ref; - let substs = Substs::bound_vars(&generics); + let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); let inner = Ty::from_hir(&ctx, type_ref.as_ref().unwrap_or(&TypeRef::Error)); Binders::new(substs.len(), inner) } diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 0a889f805..36f53b264 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1210,6 +1210,42 @@ fn test(x: dyn Trait, y: &dyn Trait) { ); } +#[test] +fn dyn_trait_in_impl() { + assert_snapshot!( + infer(r#" +trait Trait { + fn foo(&self) -> (T, U); +} +struct S {} +impl S { + fn bar(&self) -> &dyn Trait { loop {} } +} +trait Trait2 { + fn baz(&self) -> (T, U); +} +impl Trait2 for dyn Trait { } + +fn test(s: S) { + s.bar().baz(); +} +"#), + @r###" + [33; 37) 'self': &Self + [103; 107) 'self': &S + [129; 140) '{ loop {} }': &dyn Trait + [131; 138) 'loop {}': ! + [136; 138) '{}': () + [176; 180) 'self': &Self + [252; 253) 's': S + [268; 290) '{ ...z(); }': () + [274; 275) 's': S + [274; 281) 's.bar()': &dyn Trait + [274; 287) 's.bar().baz()': (u32, i32) + "### + ); +} + #[test] fn dyn_trait_bare() { assert_snapshot!( diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index 60d70d18e..e00a82db2 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs @@ -17,7 +17,7 @@ use ra_db::{ use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation}; use crate::{ db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics, - ApplicationTy, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, + ApplicationTy, DebruijnIndex, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, }; pub(super) mod tls; @@ -815,7 +815,7 @@ pub(crate) fn associated_ty_data_query( // Lower bounds -- we could/should maybe move this to a separate query in `lower` let type_alias_data = db.type_alias_data(type_alias); let generic_params = generics(db.upcast(), type_alias.into()); - let bound_vars = Substs::bound_vars(&generic_params); + let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast()); let ctx = crate::TyLoweringContext::new(db, &resolver) .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable); @@ -849,7 +849,7 @@ pub(crate) fn trait_datum_query( let trait_data = db.trait_data(trait_); debug!("trait {:?} = {:?}", trait_id, trait_data.name); let generic_params = generics(db.upcast(), trait_.into()); - let bound_vars = Substs::bound_vars(&generic_params); + let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); let flags = chalk_rust_ir::TraitFlags { auto: trait_data.auto, upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate != krate, @@ -888,7 +888,7 @@ pub(crate) fn struct_datum_query( .as_generic_def() .map(|generic_def| { let generic_params = generics(db.upcast(), generic_def); - let bound_vars = Substs::bound_vars(&generic_params); + let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); convert_where_clauses(db, generic_def, &bound_vars) }) .unwrap_or_else(Vec::new); @@ -934,7 +934,7 @@ fn impl_def_datum( let impl_data = db.impl_data(impl_id); let generic_params = generics(db.upcast(), impl_id.into()); - let bound_vars = Substs::bound_vars(&generic_params); + let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); let trait_ = trait_ref.trait_; let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate == krate { chalk_rust_ir::ImplType::Local -- cgit v1.2.3 From 8a51a74556c77d7f228867dd64cd6244bed82be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sat, 18 Apr 2020 10:53:48 +0300 Subject: Omit more parameter hints in the presence of underscores --- crates/ra_ide/src/inlay_hints.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 45b9f7802..0774fa0a1 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -237,7 +237,8 @@ fn should_show_param_hint( ) -> bool { if param_name.is_empty() || is_argument_similar_to_param(argument, param_name) - || Some(param_name) == fn_signature.name.as_ref().map(String::as_str) + || Some(param_name.trim_start_matches('_')) + == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_')) { return false; } @@ -255,6 +256,8 @@ fn should_show_param_hint( fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool { let argument_string = remove_ref(argument.clone()).syntax().to_string(); + let param_name = param_name.trim_start_matches('_'); + let argument_string = argument_string.trim_start_matches('_'); argument_string.starts_with(¶m_name) || argument_string.ends_with(¶m_name) } @@ -1094,8 +1097,10 @@ struct Param {} fn different_order(param: &Param) {} fn different_order_mut(param: &mut Param) {} +fn has_underscore(_param: bool) {} fn twiddle(twiddle: bool) {} +fn doo(_doo: bool) {} fn main() { let container: TestVarContainer = TestVarContainer { test_var: 42 }; @@ -1112,11 +1117,15 @@ fn main() { test_processed.frob(false); twiddle(true); + doo(true); let param_begin: Param = Param {}; different_order(¶m_begin); different_order(&mut param_begin); + let param: bool = true; + has_underscore(param); + let a: f64 = 7.0; let b: f64 = 4.0; let _: f64 = a.div_euclid(b); -- cgit v1.2.3 From b49ecafd40f3dd6c9c55d14c392b8c10ce682b84 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 18 Apr 2020 11:38:58 +0200 Subject: find_path: Builtins are always in scope Fixes #3977. --- crates/ra_hir_def/src/builtin_type.rs | 54 +++++++++++++++++++---------------- crates/ra_hir_def/src/find_path.rs | 21 +++++++++++++- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/crates/ra_hir_def/src/builtin_type.rs b/crates/ra_hir_def/src/builtin_type.rs index d14901a9b..0f872b5c0 100644 --- a/crates/ra_hir_def/src/builtin_type.rs +++ b/crates/ra_hir_def/src/builtin_type.rs @@ -5,7 +5,7 @@ use std::fmt; -use hir_expand::name::{name, Name}; +use hir_expand::name::{name, AsName, Name}; #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum Signedness { @@ -75,33 +75,39 @@ impl BuiltinType { ]; } -impl fmt::Display for BuiltinType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let type_name = match self { - BuiltinType::Char => "char", - BuiltinType::Bool => "bool", - BuiltinType::Str => "str", +impl AsName for BuiltinType { + fn as_name(&self) -> Name { + match self { + BuiltinType::Char => name![char], + BuiltinType::Bool => name![bool], + BuiltinType::Str => name![str], BuiltinType::Int(BuiltinInt { signedness, bitness }) => match (signedness, bitness) { - (Signedness::Signed, IntBitness::Xsize) => "isize", - (Signedness::Signed, IntBitness::X8) => "i8", - (Signedness::Signed, IntBitness::X16) => "i16", - (Signedness::Signed, IntBitness::X32) => "i32", - (Signedness::Signed, IntBitness::X64) => "i64", - (Signedness::Signed, IntBitness::X128) => "i128", - - (Signedness::Unsigned, IntBitness::Xsize) => "usize", - (Signedness::Unsigned, IntBitness::X8) => "u8", - (Signedness::Unsigned, IntBitness::X16) => "u16", - (Signedness::Unsigned, IntBitness::X32) => "u32", - (Signedness::Unsigned, IntBitness::X64) => "u64", - (Signedness::Unsigned, IntBitness::X128) => "u128", + (Signedness::Signed, IntBitness::Xsize) => name![isize], + (Signedness::Signed, IntBitness::X8) => name![i8], + (Signedness::Signed, IntBitness::X16) => name![i16], + (Signedness::Signed, IntBitness::X32) => name![i32], + (Signedness::Signed, IntBitness::X64) => name![i64], + (Signedness::Signed, IntBitness::X128) => name![i128], + + (Signedness::Unsigned, IntBitness::Xsize) => name![usize], + (Signedness::Unsigned, IntBitness::X8) => name![u8], + (Signedness::Unsigned, IntBitness::X16) => name![u16], + (Signedness::Unsigned, IntBitness::X32) => name![u32], + (Signedness::Unsigned, IntBitness::X64) => name![u64], + (Signedness::Unsigned, IntBitness::X128) => name![u128], }, BuiltinType::Float(BuiltinFloat { bitness }) => match bitness { - FloatBitness::X32 => "f32", - FloatBitness::X64 => "f64", + FloatBitness::X32 => name![f32], + FloatBitness::X64 => name![f64], }, - }; - f.write_str(type_name) + } + } +} + +impl fmt::Display for BuiltinType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let type_name = self.as_name(); + type_name.fmt(f) } } diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index d58ac6ba5..81eff5bfe 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -7,7 +7,7 @@ use crate::{ visibility::Visibility, CrateId, ModuleDefId, ModuleId, }; -use hir_expand::name::{known, Name}; +use hir_expand::name::{known, AsName, Name}; use test_utils::tested_by; const MAX_PATH_LEN: usize = 15; @@ -113,6 +113,11 @@ fn find_path_inner( } } + // - if the item is a builtin, it's in scope + if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item { + return Some(ModPath::from_segments(PathKind::Plain, vec![builtin.as_name()])); + } + // Recursive case: // - if the item is an enum variant, refer to it via the enum if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() { @@ -523,4 +528,18 @@ mod tests { "#; check_found_path(code, "megaalloc::Arc"); } + + #[test] + fn builtins_are_in_scope() { + let code = r#" + //- /main.rs + <|> + + pub mod primitive { + pub use u8; + } + "#; + check_found_path(code, "u8"); + check_found_path(code, "u16"); + } } -- cgit v1.2.3