| use crate::loader::SearchDirectory; |
| use crate::raw::DefKind; |
| use crate::{AnalysisHost, AnalysisLoader}; |
| |
| use std::collections::HashSet; |
| use std::path::{Path, PathBuf}; |
| |
| #[derive(Clone, new)] |
| struct TestAnalysisLoader { |
| path: PathBuf, |
| } |
| |
| impl AnalysisLoader for TestAnalysisLoader { |
| fn needs_hard_reload(&self, _path_prefix: &Path) -> bool { |
| true |
| } |
| |
| fn fresh_host(&self) -> AnalysisHost<Self> { |
| AnalysisHost::new_with_loader(self.clone()) |
| } |
| |
| fn set_path_prefix(&mut self, _path_prefix: &Path) {} |
| |
| fn abs_path_prefix(&self) -> Option<PathBuf> { |
| panic!(); |
| } |
| |
| fn search_directories(&self) -> Vec<SearchDirectory> { |
| vec![SearchDirectory::new(self.path.clone(), None)] |
| } |
| } |
| |
| #[test] |
| fn doc_urls_resolve_correctly() { |
| let host = AnalysisHost::new_with_loader(TestAnalysisLoader::new( |
| Path::new("test_data/rust-analysis").to_owned(), |
| )); |
| host.reload(Path::new("test_data/rust-analysis"), Path::new("test_data/rust-analysis")) |
| .unwrap(); |
| |
| fn assert_url_for_type<S: Into<Option<&'static str>>>( |
| host: &AnalysisHost<TestAnalysisLoader>, |
| type_: &str, |
| qualname: S, |
| url: &str, |
| ) { |
| let qualname = qualname.into(); |
| let ids = host.search_for_id(type_).unwrap(); |
| let defs: Vec<_> = ids |
| .into_iter() |
| .map(|id| host.get_def(id).unwrap()) |
| .filter(|def| qualname.is_none() || def.qualname == qualname.unwrap()) |
| .collect(); |
| trace!("{}: {:#?}", type_, defs); |
| assert_eq!(defs.len(), 1); |
| assert_eq!(host.doc_url(&defs[0].span), Ok(url.into())); |
| } |
| |
| // FIXME This test cannot work for some values |
| // Primitives like i64. i64 is shown with type mod but requires name "primitive". |
| // All methods (instead of trait methods, see as_mut), seem to only be available for generic qualname |
| // Unions like ManuallyDrop are not in the analysis file, just methods implemented for them or methods using them |
| |
| assert_url_for_type( |
| &host, |
| "MAIN_SEPARATOR", |
| None, |
| "https://doc.rust-lang.org/nightly/std/path/MAIN_SEPARATOR.v.html", |
| ); |
| // the parent has a qualname which is not represented in the usage, the ip part |
| assert_url_for_type( |
| &host, |
| "Ipv4Addr", |
| None, |
| "https://doc.rust-lang.org/nightly/std/net/ip/Ipv4Addr.t.html", |
| ); |
| assert_url_for_type( |
| &host, |
| "VarError", |
| None, |
| "https://doc.rust-lang.org/nightly/std/env/VarError.t.html", |
| ); |
| assert_url_for_type( |
| &host, |
| "NotPresent", |
| None, |
| "https://doc.rust-lang.org/nightly/std/env/VarError.t.html#NotPresent.v", |
| ); |
| assert_url_for_type( |
| &host, |
| "Result", |
| "std::thread::Result", |
| "https://doc.rust-lang.org/nightly/std/thread/Result.t.html", |
| ); |
| assert_url_for_type( |
| &host, |
| "args", |
| "std::env::args", |
| "https://doc.rust-lang.org/nightly/std/env/args.v.html", |
| ); |
| assert_url_for_type( |
| &host, |
| "AsciiExt", |
| None, |
| "https://doc.rust-lang.org/nightly/std/ascii/AsciiExt.t.html", |
| ); |
| assert_url_for_type( |
| &host, |
| "is_ascii", |
| "std::ascii::AsciiExt::is_ascii", |
| "https://doc.rust-lang.org/nightly/std/ascii/AsciiExt.t.html#is_ascii.v", |
| ); |
| assert_url_for_type( |
| &host, |
| "status", |
| "std::process::Output::status", |
| "https://doc.rust-lang.org/nightly/std/process/Output.t.html#status.v", |
| ); |
| assert_url_for_type( |
| &host, |
| "copy", |
| "std::fs::copy", |
| "https://doc.rust-lang.org/nightly/std/fs/copy.v.html", |
| ); |
| // prelude and fs are both mod, but the parent once has a trailing / and once not |
| assert_url_for_type( |
| &host, |
| "prelude", |
| "std::io::prelude", |
| "https://doc.rust-lang.org/nightly/std/io/prelude/", |
| ); |
| assert_url_for_type(&host, "fs", "std::fs", "https://doc.rust-lang.org/nightly/std/fs/"); |
| } |
| |
| #[test] |
| fn smoke() { |
| // Read in test data and lower it, check we don't crash. |
| let host = AnalysisHost::new_with_loader(TestAnalysisLoader::new( |
| Path::new("test_data/rls-analysis").to_owned(), |
| )); |
| host.reload(Path::new("test_data/rls-analysis"), Path::new("test_data/rls-analysis")).unwrap(); |
| } |
| |
| #[test] |
| fn test_hello() { |
| // Simple program, a somewhat thorough test that we have all the defs and refs we expect. |
| let host = AnalysisHost::new_with_loader(TestAnalysisLoader::new( |
| Path::new("test_data/hello/save-analysis").to_owned(), |
| )); |
| host.reload(Path::new("test_data/hello"), Path::new("test_data/hello")).unwrap(); |
| |
| let ids = host.search_for_id("print_hello").unwrap(); |
| assert_eq!(ids.len(), 1); |
| let id = ids[0]; |
| let def = host.get_def(id).unwrap(); |
| assert_eq!(def.name, "print_hello"); |
| assert_eq!(def.kind, DefKind::Function); |
| let refs = host.find_all_refs_by_id(id).unwrap(); |
| assert_eq!(refs.len(), 2); |
| assert_eq!(refs[0].file, Path::new("test_data/hello/src/main.rs")); |
| assert_eq!(refs[0].range.row_start.0, 0); |
| assert_eq!(refs[1].file, Path::new("test_data/hello/src/main.rs")); |
| assert_eq!(refs[1].range.row_start.0, 6); |
| let refs = host.search("print_hello").unwrap(); |
| assert_eq!(refs.len(), 2); |
| assert_eq!(refs[0].file, Path::new("test_data/hello/src/main.rs")); |
| assert_eq!(refs[0].range.row_start.0, 0); |
| assert_eq!(refs[1].file, Path::new("test_data/hello/src/main.rs")); |
| assert_eq!(refs[1].range.row_start.0, 6); |
| |
| let ids = host.search_for_id("main").unwrap(); |
| assert_eq!(ids.len(), 1); |
| let id = ids[0]; |
| let def = host.get_def(id).unwrap(); |
| assert_eq!(def.name, "main"); |
| assert_eq!(def.kind, DefKind::Function); |
| let refs = host.find_all_refs_by_id(id).unwrap(); |
| assert_eq!(refs.len(), 1); |
| assert_eq!(refs[0].file, Path::new("test_data/hello/src/main.rs")); |
| assert_eq!(refs[0].range.row_start.0, 5); |
| let refs = host.search("main").unwrap(); |
| assert_eq!(refs.len(), 1); |
| assert_eq!(refs[0].file, Path::new("test_data/hello/src/main.rs")); |
| assert_eq!(refs[0].range.row_start.0, 5); |
| |
| let ids = host.search_for_id("name").unwrap(); |
| assert_eq!(ids.len(), 1); |
| let id = ids[0]; |
| let def = host.get_def(id).unwrap(); |
| assert_eq!(def.name, "name"); |
| assert_eq!(def.kind, DefKind::Local); |
| let refs = host.find_all_refs_by_id(id).unwrap(); |
| assert_eq!(refs.len(), 2); |
| assert_eq!(refs[0].file, Path::new("test_data/hello/src/main.rs")); |
| assert_eq!(refs[0].range.row_start.0, 1); |
| assert_eq!(refs[1].file, Path::new("test_data/hello/src/main.rs")); |
| assert_eq!(refs[1].range.row_start.0, 2); |
| let refs = host.search("name").unwrap(); |
| assert_eq!(refs.len(), 2); |
| assert_eq!(refs[0].file, Path::new("test_data/hello/src/main.rs")); |
| assert_eq!(refs[0].range.row_start.0, 1); |
| assert_eq!(refs[1].file, Path::new("test_data/hello/src/main.rs")); |
| assert_eq!(refs[1].range.row_start.0, 2); |
| |
| let defs = host.matching_defs("print_hello").unwrap(); |
| assert_eq!(defs.len(), 1); |
| let hello_def = &defs[0]; |
| assert_eq!(hello_def.name, "print_hello"); |
| assert_eq!(hello_def.kind, DefKind::Function); |
| assert_eq!(hello_def.span.range.row_start.0, 0); |
| |
| let defs = host.matching_defs("main").unwrap(); |
| assert_eq!(defs.len(), 1); |
| let main_def = &defs[0]; |
| assert_eq!(main_def.name, "main"); |
| assert_eq!(main_def.kind, DefKind::Function); |
| assert_eq!(main_def.span.range.row_start.0, 5); |
| |
| let defs = host.matching_defs("name").unwrap(); |
| assert_eq!(defs.len(), 1); |
| let matching_def = &defs[0]; |
| assert_eq!(matching_def.name, "name"); |
| assert_eq!(matching_def.kind, DefKind::Local); |
| assert_eq!(matching_def.span.range.row_start.0, 1); |
| |
| assert_eq!(host.matching_defs("goodbye").unwrap().len(), 0); |
| assert_eq!(host.matching_defs("mäin").unwrap().len(), 0); |
| |
| let pri_matches = host.matching_defs("pri").unwrap(); |
| let print_hello_matches = host.matching_defs("print_hello").unwrap(); |
| assert_eq!(1, pri_matches.len()); |
| assert_eq!(1, print_hello_matches.len()); |
| let pri_f = &pri_matches[0]; |
| let print_hello_f = &print_hello_matches[0]; |
| assert_eq!(pri_f.name, print_hello_f.name); |
| assert_eq!(pri_f.kind, print_hello_f.kind); |
| |
| let all_matches = |
| host.matching_defs("").unwrap().iter().map(|d| d.name.to_owned()).collect::<HashSet<_>>(); |
| |
| let expected_matches = |
| ["main", "name", "print_hello"].iter().map(|&m| String::from(m)).collect::<HashSet<_>>(); |
| assert_eq!(all_matches, expected_matches); |
| } |
| |
| // TODO |
| // check span functions |
| // check complex programs |
| |
| #[test] |
| fn test_types() { |
| fn assert_type( |
| host: &AnalysisHost<TestAnalysisLoader>, |
| name: &str, |
| def_kind: DefKind, |
| expect_lines: &[u32], |
| ) { |
| let ids = host.search_for_id(name).unwrap(); |
| println!("name: {}", name); |
| assert_eq!(ids.len(), 1); |
| |
| let id = ids[0]; |
| let def = host.get_def(id).unwrap(); |
| assert_eq!(def.name, name); |
| assert_eq!(def.kind, def_kind); |
| |
| let refs = host.find_all_refs_by_id(id).unwrap(); |
| assert_eq!(refs.len(), expect_lines.len()); |
| |
| for (i, start) in expect_lines.iter().enumerate() { |
| assert_eq!(refs[i].file, Path::new("test_data/types/src/main.rs")); |
| assert_eq!(refs[i].range.row_start.0 + 1, *start); |
| } |
| } |
| |
| let host = AnalysisHost::new_with_loader(TestAnalysisLoader::new( |
| Path::new("test_data/types/save-analysis").to_owned(), |
| )); |
| host.reload(Path::new("test_data/types"), Path::new("test_data/types")).unwrap(); |
| |
| assert_type(&host, "Foo", DefKind::Struct, &[1, 6, 7, 10, 10]); |
| assert_type(&host, "f", DefKind::Field, &[2, 6]); |
| assert_type(&host, "main", DefKind::Function, &[5]); |
| assert_type(&host, "test_binding", DefKind::Local, &[11]); |
| assert_type(&host, "TEST_CONST", DefKind::Const, &[12]); |
| assert_type(&host, "TEST_STATIC", DefKind::Static, &[13]); |
| assert_type(&host, "test_module", DefKind::Mod, &[17]); |
| assert_type(&host, "TestType", DefKind::Type, &[18]); |
| assert_type(&host, "TestUnion", DefKind::Union, &[21]); |
| assert_type(&host, "TestTrait", DefKind::Trait, &[25]); |
| assert_type(&host, "test_method", DefKind::Method, &[26]); |
| assert_type(&host, "FooEnum", DefKind::Enum, &[29]); |
| assert_type(&host, "TupleVariant", DefKind::TupleVariant, &[30]); |
| assert_type(&host, "StructVariant", DefKind::StructVariant, &[31]); |
| |
| let t_matches = host.matching_defs("t").unwrap(); |
| let t_names = t_matches.iter().map(|m| m.name.to_owned()).collect::<HashSet<_>>(); |
| let expected_t_names = [ |
| "TEST_CONST", |
| "TEST_STATIC", |
| "TestTrait", |
| "TestType", |
| "TestUnion", |
| "TupleVariant", |
| "test_binding", |
| "test_method", |
| "test_module", |
| ] |
| .iter() |
| .map(|&n| String::from(n)) |
| .collect::<HashSet<_>>(); |
| |
| assert_eq!(t_names, expected_t_names); |
| |
| let upper_matches = host.matching_defs("FOOENUM").unwrap(); |
| let lower_matches = host.matching_defs("fooenum").unwrap(); |
| assert_eq!(upper_matches[0].name, "FooEnum"); |
| assert_eq!(lower_matches[0].name, "FooEnum"); |
| } |
| |
| #[test] |
| fn test_child_count() { |
| let host = AnalysisHost::new_with_loader(TestAnalysisLoader::new( |
| Path::new("test_data/types/save-analysis").to_owned(), |
| )); |
| host.reload(Path::new("test_data/types"), Path::new("test_data/types")).unwrap(); |
| |
| let ids = host.search_for_id("Foo").unwrap(); |
| let id = ids[0]; |
| assert_eq!(host.for_each_child_def(id, |id, _| id).unwrap().len(), 1); |
| } |
| |
| #[test] |
| fn test_self() { |
| let host = AnalysisHost::new_with_loader(TestAnalysisLoader::new( |
| Path::new("test_data/exprs/save-analysis").to_owned(), |
| )); |
| host.reload(Path::new("test_data/exprs"), Path::new("test_data/exprs")).unwrap(); |
| |
| let spans = host.search("self").unwrap(); |
| assert_eq!(spans.len(), 2); |
| let def = host.goto_def(&spans[1]); |
| assert_eq!(def.unwrap(), spans[0]); |
| } |
| |
| #[test] |
| fn test_extern_fn() { |
| let host = AnalysisHost::new_with_loader(TestAnalysisLoader::new( |
| Path::new("test_data/exprs/save-analysis").to_owned(), |
| )); |
| host.reload(Path::new("test_data/exprs"), Path::new("test_data/exprs")).unwrap(); |
| |
| let spans = host.search("foo").unwrap(); |
| assert_eq!(spans.len(), 2); |
| let def = host.goto_def(&spans[1]); |
| assert_eq!(def.unwrap(), spans[0]); |
| } |
| |
| #[test] |
| fn test_all_ref_unique() { |
| let host = AnalysisHost::new_with_loader(TestAnalysisLoader::new( |
| Path::new("test_data/rename/save-analysis").to_owned(), |
| )); |
| host.reload(Path::new("test_data/rename"), Path::new("test_data/rename")).unwrap(); |
| |
| let spans = host.search("bar").unwrap(); |
| assert_eq!(spans.len(), 4); |
| let refs = host.find_all_refs(&spans[3], true, true); |
| assert_eq!(refs.unwrap().len(), 0); |
| |
| let spans = host.search("qux").unwrap(); |
| assert_eq!(spans.len(), 3); |
| let refs = host.find_all_refs(&spans[2], true, true); |
| assert_eq!(refs.unwrap().len(), 3); |
| } |