diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 2b091f37a0b..9baae921445 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs @@ -10,9 +10,10 @@ use vfs::{file_set::FileSet, VfsPath}; use crate::{ - input::CrateName, Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, - FileId, FilePosition, FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, - SourceDatabaseExt, SourceRoot, SourceRootId, + input::{CrateName, CrateOrigin}, + Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition, + FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, SourceDatabaseExt, + SourceRoot, SourceRootId, }; pub const WORKSPACE: SourceRootId = SourceRootId(0); @@ -130,17 +131,18 @@ pub fn parse(ra_fixture: &str) -> ChangeFixture { current_source_root_kind = *kind; } - if let Some(krate) = meta.krate { + if let Some((krate, origin, version)) = meta.krate { let crate_name = CrateName::normalize_dashes(&krate); let crate_id = crate_graph.add_crate_root( file_id, meta.edition, Some(crate_name.clone().into()), - None, + version, meta.cfg.clone(), meta.cfg, meta.env, Default::default(), + origin, ); let prev = crates.insert(crate_name.clone(), crate_id); assert!(prev.is_none()); @@ -174,6 +176,7 @@ pub fn parse(ra_fixture: &str) -> ChangeFixture { default_cfg, Env::default(), Default::default(), + Default::default(), ); } else { for (from, to, prelude) in crate_deps { @@ -209,6 +212,7 @@ pub fn parse(ra_fixture: &str) -> ChangeFixture { CfgOptions::default(), Env::default(), Vec::new(), + CrateOrigin::Lang, ); for krate in all_crates { @@ -243,6 +247,7 @@ pub fn parse(ra_fixture: &str) -> ChangeFixture { CfgOptions::default(), Env::default(), proc_macro, + CrateOrigin::Lang, ); for krate in all_crates { @@ -324,7 +329,7 @@ enum SourceRootKind { #[derive(Debug)] struct FileMeta { path: String, - krate: Option, + krate: Option<(String, CrateOrigin, Option)>, deps: Vec, extern_prelude: Vec, cfg: CfgOptions, @@ -333,16 +338,32 @@ struct FileMeta { introduce_new_source_root: Option, } +fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option) { + if let Some((a, b)) = crate_str.split_once("@") { + let (version, origin) = match b.split_once(":") { + Some(("CratesIo", data)) => match data.split_once(",") { + Some((version, url)) => { + (version, CrateOrigin::CratesIo { repo: Some(url.to_owned()) }) + } + _ => panic!("Bad crates.io parameter: {}", data), + }, + _ => panic!("Bad string for crate origin: {}", b), + }; + (a.to_owned(), origin, Some(version.to_string())) + } else { + (crate_str, CrateOrigin::Unknown, None) + } +} + impl From for FileMeta { fn from(f: Fixture) -> FileMeta { let mut cfg = CfgOptions::default(); f.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into())); f.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into())); - let deps = f.deps; FileMeta { path: f.path, - krate: f.krate, + krate: f.krate.map(parse_crate), extern_prelude: f.extern_prelude.unwrap_or_else(|| deps.clone()), deps, cfg, diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs index 98533ca0a72..f182427e478 100644 --- a/crates/base_db/src/input.rs +++ b/crates/base_db/src/input.rs @@ -112,6 +112,24 @@ fn deref(&self) -> &str { } } +/// Origin of the crates. It is used in emitting monikers. +#[derive(Debug, Clone)] +pub enum CrateOrigin { + /// Crates that are from crates.io official registry, + CratesIo { repo: Option }, + /// Crates that are provided by the language, like std, core, proc-macro, ... + Lang, + /// Crates that we don't know their origin. + // Idealy this enum should cover all cases, and then we remove this variant. + Unknown, +} + +impl Default for CrateOrigin { + fn default() -> Self { + Self::Unknown + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateDisplayName { // The name we use to display various paths (with `_`). @@ -205,6 +223,7 @@ pub struct CrateData { pub env: Env, pub dependencies: Vec, pub proc_macro: Vec, + pub origin: CrateOrigin, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -256,6 +275,7 @@ pub fn add_crate_root( potential_cfg_options: CfgOptions, env: Env, proc_macro: Vec, + origin: CrateOrigin, ) -> CrateId { let data = CrateData { root_file_id: file_id, @@ -267,6 +287,7 @@ pub fn add_crate_root( env, proc_macro, dependencies: Vec::new(), + origin, }; let crate_id = CrateId(self.arena.len() as u32); let prev = self.arena.insert(crate_id, data); @@ -571,6 +592,7 @@ fn detect_cyclic_dependency_indirect() { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); let crate2 = graph.add_crate_root( FileId(2u32), @@ -581,6 +603,7 @@ fn detect_cyclic_dependency_indirect() { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); let crate3 = graph.add_crate_root( FileId(3u32), @@ -591,6 +614,7 @@ fn detect_cyclic_dependency_indirect() { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); assert!(graph .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) @@ -615,6 +639,7 @@ fn detect_cyclic_dependency_direct() { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); let crate2 = graph.add_crate_root( FileId(2u32), @@ -625,6 +650,7 @@ fn detect_cyclic_dependency_direct() { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); assert!(graph .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) @@ -646,6 +672,7 @@ fn it_works() { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); let crate2 = graph.add_crate_root( FileId(2u32), @@ -656,6 +683,7 @@ fn it_works() { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); let crate3 = graph.add_crate_root( FileId(3u32), @@ -666,6 +694,7 @@ fn it_works() { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); assert!(graph .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) @@ -687,6 +716,7 @@ fn dashes_are_normalized() { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); let crate2 = graph.add_crate_root( FileId(2u32), @@ -697,6 +727,7 @@ fn dashes_are_normalized() { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); assert!(graph .add_dep( diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index c34a3295404..d4070457cdd 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs @@ -11,9 +11,9 @@ pub use crate::{ change::Change, input::{ - CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, - ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroId, ProcMacroKind, - SourceRoot, SourceRootId, + CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, + Edition, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroId, + ProcMacroKind, SourceRoot, SourceRootId, }, }; pub use salsa::{self, Cancelled}; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 3946f516421..badd9ac5891 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -34,7 +34,7 @@ use std::{iter, ops::ControlFlow, sync::Arc}; use arrayvec::ArrayVec; -use base_db::{CrateDisplayName, CrateId, Edition, FileId}; +use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId}; use either::Either; use hir_def::{ adt::{ReprKind, VariantData}, @@ -144,6 +144,10 @@ pub struct CrateDependency { } impl Crate { + pub fn origin(self, db: &dyn HirDatabase) -> CrateOrigin { + db.crate_graph()[self.id].origin.clone() + } + pub fn dependencies(self, db: &dyn HirDatabase) -> Vec { db.crate_graph()[self.id] .dependencies diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 034a5117933..121baa86f1e 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -41,6 +41,7 @@ macro_rules! eprintln { mod join_lines; mod markdown_remove; mod matching_brace; +mod moniker; mod move_item; mod parent_module; mod references; @@ -83,6 +84,7 @@ macro_rules! eprintln { inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, join_lines::JoinLinesConfig, markup::Markup, + moniker::{MonikerKind, MonikerResult, PackageInformation}, move_item::Direction, navigation_target::NavigationTarget, prime_caches::PrimeCachesProgress, @@ -225,6 +227,7 @@ pub fn from_single_file(text: String) -> (Analysis, FileId) { cfg_options, Env::default(), Default::default(), + Default::default(), ); change.change_file(file_id, Some(Arc::new(text))); change.set_crate_graph(crate_graph); @@ -425,6 +428,14 @@ pub fn hover( self.with_db(|db| hover::hover(db, range, config)) } + /// Returns moniker of symbol at position. + pub fn moniker( + &self, + position: FilePosition, + ) -> Cancellable>>> { + self.with_db(|db| moniker::moniker(db, position)) + } + /// Return URL(s) for the documentation of the symbol under the cursor. pub fn external_docs( &self, diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs new file mode 100644 index 00000000000..9d8c742fc46 --- /dev/null +++ b/crates/ide/src/moniker.rs @@ -0,0 +1,233 @@ +//! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports) +//! for LSIF and LSP. + +use hir::{db::DefDatabase, Crate, Name, Semantics}; +use ide_db::{ + base_db::{CrateOrigin, FileId, FileLoader, FilePosition}, + defs::Definition, + helpers::pick_best_token, + RootDatabase, +}; +use itertools::Itertools; +use syntax::{AstNode, SyntaxKind::*, T}; + +use crate::{doc_links::token_as_doc_comment, RangeInfo}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct MonikerIdentifier { + crate_name: String, + path: Vec, +} + +impl ToString for MonikerIdentifier { + fn to_string(&self) -> String { + match self { + MonikerIdentifier { path, crate_name } => { + format!("{}::{}", crate_name, path.iter().map(|x| x.to_string()).join("::")) + } + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum MonikerKind { + Import, + Export, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct MonikerResult { + pub identifier: MonikerIdentifier, + pub kind: MonikerKind, + pub package_information: PackageInformation, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PackageInformation { + pub name: String, + pub repo: String, + pub version: String, +} + +pub(crate) fn crate_for_file(db: &RootDatabase, file_id: FileId) -> Option { + for &krate in db.relevant_crates(file_id).iter() { + let crate_def_map = db.crate_def_map(krate); + for (_, data) in crate_def_map.modules() { + if data.origin.file_id() == Some(file_id) { + return Some(krate.into()); + } + } + } + None +} + +pub(crate) fn moniker( + db: &RootDatabase, + FilePosition { file_id, offset }: FilePosition, +) -> Option>> { + let sema = &Semantics::new(db); + let file = sema.parse(file_id).syntax().clone(); + let current_crate = crate_for_file(db, file_id)?; + let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { + IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | COMMENT => 2, + kind if kind.is_trivia() => 0, + _ => 1, + })?; + if let Some(doc_comment) = token_as_doc_comment(&original_token) { + return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, _| { + let m = def_to_moniker(db, def, current_crate)?; + Some(RangeInfo::new(original_token.text_range(), vec![m])) + }); + } + let navs = sema + .descend_into_macros(original_token.clone()) + .into_iter() + .map(|token| { + Definition::from_token(sema, &token) + .into_iter() + .flat_map(|def| def_to_moniker(sema.db, def, current_crate)) + .collect::>() + }) + .flatten() + .unique() + .collect::>(); + Some(RangeInfo::new(original_token.text_range(), navs)) +} + +pub(crate) fn def_to_moniker( + db: &RootDatabase, + def: Definition, + from_crate: Crate, +) -> Option { + if matches!(def, Definition::GenericParam(_) | Definition::SelfType(_) | Definition::Local(_)) { + return None; + } + let module = def.module(db)?; + let krate = module.krate(); + let mut path = vec![]; + path.extend(module.path_to_root(db).into_iter().filter_map(|x| x.name(db))); + if let Definition::Field(it) = def { + path.push(it.parent_def(db).name(db)); + } + path.push(def.name(db)?); + Some(MonikerResult { + identifier: MonikerIdentifier { + crate_name: krate.display_name(db)?.crate_name().to_string(), + path, + }, + kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import }, + package_information: { + let name = krate.display_name(db)?.to_string(); + let (repo, version) = match krate.origin(db) { + CrateOrigin::CratesIo { repo } => (repo?, krate.version(db)?), + CrateOrigin::Lang => ( + "https://github.com/rust-lang/rust/".to_string(), + "compiler_version".to_string(), + ), + CrateOrigin::Unknown => return None, + }; + PackageInformation { name, repo, version } + }, + }) +} + +#[cfg(test)] +mod tests { + use crate::fixture; + + use super::MonikerKind; + + #[track_caller] + fn no_moniker(ra_fixture: &str) { + let (analysis, position) = fixture::position(ra_fixture); + if let Some(x) = analysis.moniker(position).unwrap() { + assert_eq!(x.info.len(), 0, "Moniker founded but no moniker expected: {:?}", x); + } + } + + #[track_caller] + fn check_moniker(ra_fixture: &str, identifier: &str, package: &str, kind: MonikerKind) { + let (analysis, position) = fixture::position(ra_fixture); + let x = analysis.moniker(position).unwrap().expect("no moniker found").info; + assert_eq!(x.len(), 1); + let x = x.into_iter().next().unwrap(); + assert_eq!(identifier, x.identifier.to_string()); + assert_eq!(package, format!("{:?}", x.package_information)); + assert_eq!(kind, x.kind); + } + + #[test] + fn basic() { + check_moniker( + r#" +//- /lib.rs crate:main deps:foo +use foo::module::func; +fn main() { + func$0(); +} +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +pub mod module { + pub fn func() {} +} +"#, + "foo::module::func", + r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + MonikerKind::Import, + ); + check_moniker( + r#" +//- /lib.rs crate:main deps:foo +use foo::module::func; +fn main() { + func(); +} +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +pub mod module { + pub fn func$0() {} +} +"#, + "foo::module::func", + r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + MonikerKind::Export, + ); + } + + #[test] + fn moniker_for_field() { + check_moniker( + r#" +//- /lib.rs crate:main deps:foo +use foo::St; +fn main() { + let x = St { a$0: 2 }; +} +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +pub struct St { + pub a: i32, +} +"#, + "foo::St::a", + r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + MonikerKind::Import, + ); + } + + #[test] + fn no_moniker_for_local() { + no_moniker( + r#" +//- /lib.rs crate:main deps:foo +use foo::module::func; +fn main() { + func(); +} +//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git +pub mod module { + pub fn func() { + let x$0 = 2; + } +} +"#, + ); + } +} diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 29eef546cef..cf98bc32faf 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -12,6 +12,7 @@ use rustc_hash::FxHashSet; use syntax::{AstNode, SyntaxKind::*, SyntaxToken, TextRange, T}; +use crate::moniker::{crate_for_file, def_to_moniker, MonikerResult}; use crate::{ hover::hover_for_definition, Analysis, Fold, HoverConfig, HoverDocFormat, HoverResult, InlayHint, InlayHintsConfig, TryToNav, @@ -40,6 +41,7 @@ pub struct TokenStaticData { pub hover: Option, pub definition: Option, pub references: Vec, + pub moniker: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -97,6 +99,7 @@ fn all_modules(db: &dyn HirDatabase) -> Vec { impl StaticIndex<'_> { fn add_file(&mut self, file_id: FileId) { + let current_crate = crate_for_file(self.db, file_id); let folds = self.analysis.folding_ranges(file_id).unwrap(); let inlay_hints = self .analysis @@ -143,6 +146,7 @@ fn add_file(&mut self, file_id: FileId) { .try_to_nav(self.db) .map(|x| FileRange { file_id: x.file_id, range: x.focus_or_full_range() }), references: vec![], + moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)), }); self.def_map.insert(def, x); x @@ -206,6 +210,7 @@ mod tests { use crate::{fixture, StaticIndex}; use ide_db::base_db::FileRange; use std::collections::HashSet; + use syntax::TextSize; fn check_all_ranges(ra_fixture: &str) { let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); @@ -231,6 +236,10 @@ fn check_definitions(ra_fixture: &str) { let mut range_set: HashSet<_> = ranges.iter().map(|x| x.0).collect(); for (_, t) in s.tokens.iter() { if let Some(x) = t.definition { + if x.range.start() == TextSize::from(0) { + // ignore definitions that are whole of file + continue; + } if !range_set.contains(&x) { panic!("additional definition {:?}", x); } @@ -262,6 +271,28 @@ enum E { X(Foo) } ); } + #[test] + fn multi_crate() { + check_definitions( + r#" +//- /main.rs crate:main deps:foo + + +use foo::func; + +fn main() { + //^^^^ + func(); +} +//- /foo/lib.rs crate:foo + +pub func() { + +} +"#, + ); + } + #[test] fn derives() { check_all_ranges( diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index 85123ed5c57..c2cf3c4ce3a 100644 --- a/crates/project_model/src/cargo_workspace.rs +++ b/crates/project_model/src/cargo_workspace.rs @@ -130,6 +130,8 @@ pub struct PackageData { pub version: semver::Version, /// Name as given in the `Cargo.toml` pub name: String, + /// Repository as given in the `Cargo.toml` + pub repository: Option, /// Path containing the `Cargo.toml` pub manifest: ManifestPath, /// Targets provided by the crate (lib, bin, example, test, ...) @@ -146,9 +148,9 @@ pub struct PackageData { pub features: FxHashMap>, /// List of features enabled on this package pub active_features: Vec, - // String representation of package id + /// String representation of package id pub id: String, - // The contents of [package.metadata.rust-analyzer] + /// The contents of [package.metadata.rust-analyzer] pub metadata: RustAnalyzerPackageMetaData, } @@ -302,7 +304,14 @@ pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace { meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); for meta_pkg in &meta.packages { let cargo_metadata::Package { - id, edition, name, manifest_path, version, metadata, .. + id, + edition, + name, + manifest_path, + version, + metadata, + repository, + .. } = meta_pkg; let meta = from_value::(metadata.clone()).unwrap_or_default(); let edition = edition.parse::().unwrap_or_else(|err| { @@ -323,6 +332,7 @@ pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace { is_local, is_member, edition, + repository: repository.clone(), dependencies: Vec::new(), features: meta_pkg.features.clone().into_iter().collect(), active_features: Vec::new(), diff --git a/crates/project_model/src/project_json.rs b/crates/project_model/src/project_json.rs index d8d0d07affc..a3c5ac16740 100644 --- a/crates/project_model/src/project_json.rs +++ b/crates/project_model/src/project_json.rs @@ -39,6 +39,7 @@ pub struct Crate { pub(crate) include: Vec, pub(crate) exclude: Vec, pub(crate) is_proc_macro: bool, + pub(crate) repository: Option, } impl ProjectJson { @@ -99,6 +100,7 @@ pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { include, exclude, is_proc_macro: crate_data.is_proc_macro, + repository: crate_data.repository, } }) .collect::>(), @@ -142,6 +144,8 @@ struct CrateData { source: Option, #[serde(default)] is_proc_macro: bool, + #[serde(default)] + repository: Option, } #[derive(Deserialize, Debug, Clone)] diff --git a/crates/project_model/src/tests.rs b/crates/project_model/src/tests.rs index 252acde14d7..edf26736618 100644 --- a/crates/project_model/src/tests.rs +++ b/crates/project_model/src/tests.rs @@ -173,6 +173,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { }, ], proc_macro: [], + origin: CratesIo { + repo: None, + }, }, CrateId( 5, @@ -242,6 +245,11 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { }, ], proc_macro: [], + origin: CratesIo { + repo: Some( + "https://github.com/rust-lang/libc", + ), + }, }, CrateId( 2, @@ -311,6 +319,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { }, ], proc_macro: [], + origin: CratesIo { + repo: None, + }, }, CrateId( 4, @@ -370,6 +381,11 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { }, dependencies: [], proc_macro: [], + origin: CratesIo { + repo: Some( + "https://github.com/rust-lang/libc", + ), + }, }, CrateId( 1, @@ -439,6 +455,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { }, ], proc_macro: [], + origin: CratesIo { + repo: None, + }, }, CrateId( 6, @@ -498,6 +517,11 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { }, dependencies: [], proc_macro: [], + origin: CratesIo { + repo: Some( + "https://github.com/rust-lang/libc", + ), + }, }, CrateId( 3, @@ -567,6 +591,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { }, ], proc_macro: [], + origin: CratesIo { + repo: None, + }, }, }, }"#]], @@ -651,6 +678,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() { }, ], proc_macro: [], + origin: CratesIo { + repo: None, + }, }, CrateId( 5, @@ -720,6 +750,11 @@ fn cargo_hello_world_project_model_with_selective_overrides() { }, ], proc_macro: [], + origin: CratesIo { + repo: Some( + "https://github.com/rust-lang/libc", + ), + }, }, CrateId( 2, @@ -791,6 +826,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() { }, ], proc_macro: [], + origin: CratesIo { + repo: None, + }, }, CrateId( 4, @@ -850,6 +888,11 @@ fn cargo_hello_world_project_model_with_selective_overrides() { }, dependencies: [], proc_macro: [], + origin: CratesIo { + repo: Some( + "https://github.com/rust-lang/libc", + ), + }, }, CrateId( 1, @@ -921,6 +964,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() { }, ], proc_macro: [], + origin: CratesIo { + repo: None, + }, }, CrateId( 6, @@ -980,6 +1026,11 @@ fn cargo_hello_world_project_model_with_selective_overrides() { }, dependencies: [], proc_macro: [], + origin: CratesIo { + repo: Some( + "https://github.com/rust-lang/libc", + ), + }, }, CrateId( 3, @@ -1051,6 +1102,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() { }, ], proc_macro: [], + origin: CratesIo { + repo: None, + }, }, }, }"#]], @@ -1126,6 +1180,9 @@ fn cargo_hello_world_project_model() { }, ], proc_macro: [], + origin: CratesIo { + repo: None, + }, }, CrateId( 5, @@ -1197,6 +1254,11 @@ fn cargo_hello_world_project_model() { }, ], proc_macro: [], + origin: CratesIo { + repo: Some( + "https://github.com/rust-lang/libc", + ), + }, }, CrateId( 2, @@ -1268,6 +1330,9 @@ fn cargo_hello_world_project_model() { }, ], proc_macro: [], + origin: CratesIo { + repo: None, + }, }, CrateId( 4, @@ -1329,6 +1394,11 @@ fn cargo_hello_world_project_model() { }, dependencies: [], proc_macro: [], + origin: CratesIo { + repo: Some( + "https://github.com/rust-lang/libc", + ), + }, }, CrateId( 1, @@ -1400,6 +1470,9 @@ fn cargo_hello_world_project_model() { }, ], proc_macro: [], + origin: CratesIo { + repo: None, + }, }, CrateId( 6, @@ -1461,6 +1534,11 @@ fn cargo_hello_world_project_model() { }, dependencies: [], proc_macro: [], + origin: CratesIo { + repo: Some( + "https://github.com/rust-lang/libc", + ), + }, }, CrateId( 3, @@ -1532,6 +1610,9 @@ fn cargo_hello_world_project_model() { }, ], proc_macro: [], + origin: CratesIo { + repo: None, + }, }, }, }"#]], @@ -1583,6 +1664,7 @@ fn rust_project_hello_world_project_model() { }, ], proc_macro: [], + origin: Lang, }, CrateId( 10, @@ -1611,6 +1693,7 @@ fn rust_project_hello_world_project_model() { }, dependencies: [], proc_macro: [], + origin: Lang, }, CrateId( 7, @@ -1639,6 +1722,7 @@ fn rust_project_hello_world_project_model() { }, dependencies: [], proc_macro: [], + origin: Lang, }, CrateId( 4, @@ -1677,6 +1761,7 @@ fn rust_project_hello_world_project_model() { }, ], proc_macro: [], + origin: Lang, }, CrateId( 1, @@ -1705,6 +1790,7 @@ fn rust_project_hello_world_project_model() { }, dependencies: [], proc_macro: [], + origin: Lang, }, CrateId( 11, @@ -1770,6 +1856,9 @@ fn rust_project_hello_world_project_model() { }, ], proc_macro: [], + origin: CratesIo { + repo: None, + }, }, CrateId( 8, @@ -1798,6 +1887,7 @@ fn rust_project_hello_world_project_model() { }, dependencies: [], proc_macro: [], + origin: Lang, }, CrateId( 5, @@ -1826,6 +1916,7 @@ fn rust_project_hello_world_project_model() { }, dependencies: [], proc_macro: [], + origin: Lang, }, CrateId( 2, @@ -1854,6 +1945,7 @@ fn rust_project_hello_world_project_model() { }, dependencies: [], proc_macro: [], + origin: Lang, }, CrateId( 9, @@ -1882,6 +1974,7 @@ fn rust_project_hello_world_project_model() { }, dependencies: [], proc_macro: [], + origin: Lang, }, CrateId( 6, @@ -1992,6 +2085,7 @@ fn rust_project_hello_world_project_model() { }, ], proc_macro: [], + origin: Lang, }, CrateId( 3, @@ -2020,6 +2114,7 @@ fn rust_project_hello_world_project_model() { }, dependencies: [], proc_macro: [], + origin: Lang, }, }, }"#]], diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index 2fc88bf5059..0335f8b1746 100644 --- a/crates/project_model/src/workspace.rs +++ b/crates/project_model/src/workspace.rs @@ -6,7 +6,8 @@ use anyhow::{format_err, Context, Result}; use base_db::{ - CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, ProcMacro, + CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env, + FileId, ProcMacro, }; use cfg::{CfgDiff, CfgOptions}; use paths::{AbsPath, AbsPathBuf}; @@ -473,6 +474,11 @@ fn project_json_to_crate_graph( cfg_options, env, proc_macro.unwrap_or_default(), + if krate.display_name.is_some() { + CrateOrigin::CratesIo { repo: krate.repository.clone() } + } else { + CrateOrigin::Unknown + }, ), ) }) @@ -681,6 +687,7 @@ fn detached_files_to_crate_graph( cfg_options.clone(), Env::default(), Vec::new(), + CrateOrigin::Unknown, ); public_deps.add(detached_file_crate, &mut crate_graph); @@ -821,7 +828,6 @@ fn add_target_crate_root( .iter() .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }), ); - crate_graph.add_crate_root( file_id, edition, @@ -831,6 +837,7 @@ fn add_target_crate_root( potential_cfg_options, env, proc_macro, + CrateOrigin::CratesIo { repo: pkg.repository.clone() }, ) } @@ -874,6 +881,7 @@ fn sysroot_to_crate_graph( cfg_options.clone(), env, proc_macro, + CrateOrigin::Lang, ); Some((krate, crate_id)) }) diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs index f108b694c01..b9bb335b05d 100644 --- a/crates/rust-analyzer/src/cli/lsif.rs +++ b/crates/rust-analyzer/src/cli/lsif.rs @@ -5,8 +5,8 @@ use std::time::Instant; use ide::{ - Analysis, FileId, FileRange, RootDatabase, StaticIndex, StaticIndexedFile, TokenId, - TokenStaticData, + Analysis, FileId, FileRange, MonikerKind, PackageInformation, RootDatabase, StaticIndex, + StaticIndexedFile, TokenId, TokenStaticData, }; use ide_db::LineIndexDatabase; @@ -36,6 +36,7 @@ struct LsifManager<'a> { token_map: HashMap, range_map: HashMap, file_map: HashMap, + package_map: HashMap, analysis: &'a Analysis, db: &'a RootDatabase, vfs: &'a Vfs, @@ -57,6 +58,7 @@ fn new<'a>(analysis: &'a Analysis, db: &'a RootDatabase, vfs: &'a Vfs) -> LsifMa token_map: HashMap::default(), range_map: HashMap::default(), file_map: HashMap::default(), + package_map: HashMap::default(), analysis, db, vfs, @@ -92,6 +94,28 @@ fn get_token_id(&mut self, id: TokenId) -> Id { result_set_id } + fn get_package_id(&mut self, package_information: PackageInformation) -> Id { + if let Some(x) = self.package_map.get(&package_information) { + return *x; + } + let pi = package_information.clone(); + let result_set_id = + self.add_vertex(lsif::Vertex::PackageInformation(lsif::PackageInformation { + name: pi.name, + manager: "cargo".to_string(), + uri: None, + content: None, + repository: Some(lsif::Repository { + url: pi.repo, + r#type: "git".to_string(), + commit_id: None, + }), + version: Some(pi.version), + })); + self.package_map.insert(package_information, result_set_id); + result_set_id + } + fn get_range_id(&mut self, id: FileRange) -> Id { if let Some(x) = self.range_map.get(&id) { return *x; @@ -146,6 +170,26 @@ fn add_token(&mut self, id: TokenId, token: TokenStaticData) { out_v: result_set_id.into(), })); } + if let Some(moniker) = token.moniker { + let package_id = self.get_package_id(moniker.package_information); + let moniker_id = self.add_vertex(lsif::Vertex::Moniker(lsp_types::Moniker { + scheme: "rust-analyzer".to_string(), + identifier: moniker.identifier.to_string(), + unique: lsp_types::UniquenessLevel::Scheme, + kind: Some(match moniker.kind { + MonikerKind::Import => lsp_types::MonikerKind::Import, + MonikerKind::Export => lsp_types::MonikerKind::Export, + }), + })); + self.add_edge(lsif::Edge::PackageInformation(lsif::EdgeData { + in_v: package_id.into(), + out_v: moniker_id.into(), + })); + self.add_edge(lsif::Edge::Moniker(lsif::EdgeData { + in_v: moniker_id.into(), + out_v: result_set_id.into(), + })); + } if let Some(def) = token.definition { let result_id = self.add_vertex(lsif::Vertex::DefinitionResult); let def_vertex = self.get_range_id(def);