about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/base_db/src/fixture.rs37
-rw-r--r--crates/base_db/src/input.rs31
-rw-r--r--crates/base_db/src/lib.rs6
-rw-r--r--crates/hir/src/lib.rs6
-rw-r--r--crates/ide/src/lib.rs11
-rw-r--r--crates/ide/src/moniker.rs233
-rw-r--r--crates/ide/src/static_index.rs31
-rw-r--r--crates/project_model/src/cargo_workspace.rs16
-rw-r--r--crates/project_model/src/project_json.rs4
-rw-r--r--crates/project_model/src/tests.rs95
-rw-r--r--crates/project_model/src/workspace.rs12
-rw-r--r--crates/rust-analyzer/src/cli/lsif.rs48
12 files changed, 511 insertions, 19 deletions
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 tt::Subtree;
 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 @@ impl 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 @@ impl ChangeFixture {
                 default_cfg,
                 Env::default(),
                 Default::default(),
+                Default::default(),
             );
         } else {
             for (from, to, prelude) in crate_deps {
@@ -209,6 +212,7 @@ impl ChangeFixture {
                 CfgOptions::default(),
                 Env::default(),
                 Vec::new(),
+                CrateOrigin::Lang,
             );
 
             for krate in all_crates {
@@ -243,6 +247,7 @@ impl 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<String>,
+    krate: Option<(String, CrateOrigin, Option<String>)>,
     deps: Vec<String>,
     extern_prelude: Vec<String>,
     cfg: CfgOptions,
@@ -333,16 +338,32 @@ struct FileMeta {
     introduce_new_source_root: Option<SourceRootKind>,
 }
 
+fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option<String>) {
+    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<Fixture> 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 @@ impl ops::Deref for CrateName {
     }
 }
 
+/// 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<String> },
+    /// 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<Dependency>,
     pub proc_macro: Vec<ProcMacro>,
+    pub origin: CrateOrigin,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -256,6 +275,7 @@ impl CrateGraph {
         potential_cfg_options: CfgOptions,
         env: Env,
         proc_macro: Vec<ProcMacro>,
+        origin: CrateOrigin,
     ) -> CrateId {
         let data = CrateData {
             root_file_id: file_id,
@@ -267,6 +287,7 @@ impl CrateGraph {
             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 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         let crate2 = graph.add_crate_root(
             FileId(2u32),
@@ -581,6 +603,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         let crate3 = graph.add_crate_root(
             FileId(3u32),
@@ -591,6 +614,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         assert!(graph
             .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@@ -615,6 +639,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         let crate2 = graph.add_crate_root(
             FileId(2u32),
@@ -625,6 +650,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         assert!(graph
             .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@@ -646,6 +672,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         let crate2 = graph.add_crate_root(
             FileId(2u32),
@@ -656,6 +683,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         let crate3 = graph.add_crate_root(
             FileId(3u32),
@@ -666,6 +694,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         assert!(graph
             .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@@ -687,6 +716,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         let crate2 = graph.add_crate_root(
             FileId(2u32),
@@ -697,6 +727,7 @@ mod tests {
             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 @@ use syntax::{ast, Parse, SourceFile, TextRange, TextSize};
 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 @@ mod display;
 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<CrateDependency> {
         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 @@ mod inlay_hints;
 mod join_lines;
 mod markdown_remove;
 mod matching_brace;
+mod moniker;
 mod move_item;
 mod parent_module;
 mod references;
@@ -83,6 +84,7 @@ pub use crate::{
     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 @@ impl Analysis {
             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 @@ impl Analysis {
         self.with_db(|db| hover::hover(db, range, config))
     }
 
+    /// Returns moniker of symbol at position.
+    pub fn moniker(
+        &self,
+        position: FilePosition,
+    ) -> Cancellable<Option<RangeInfo<Vec<moniker::MonikerResult>>>> {
+        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<Name>,
+}
+
+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<Crate> {
+    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<RangeInfo<Vec<MonikerResult>>> {
+    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::<Vec<_>>()
+        })
+        .flatten()
+        .unique()
+        .collect::<Vec<_>>();
+    Some(RangeInfo::new(original_token.text_range(), navs))
+}
+
+pub(crate) fn def_to_moniker(
+    db: &RootDatabase,
+    def: Definition,
+    from_crate: Crate,
+) -> Option<MonikerResult> {
+    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 ide_db::{
 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<HoverResult>,
     pub definition: Option<FileRange>,
     pub references: Vec<ReferenceData>,
+    pub moniker: Option<MonikerResult>,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -97,6 +99,7 @@ fn all_modules(db: &dyn HirDatabase) -> Vec<Module> {
 
 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 @@ impl StaticIndex<'_> {
                         .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 @@ mod tests {
         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);
                 }
@@ -263,6 +272,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(
             r#"
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<String>,
     /// 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<String, Vec<String>>,
     /// List of features enabled on this package
     pub active_features: Vec<String>,
-    // 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 @@ impl 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::<PackageMetadata>(metadata.clone()).unwrap_or_default();
             let edition = edition.parse::<Edition>().unwrap_or_else(|err| {
@@ -323,6 +332,7 @@ impl 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<AbsPathBuf>,
     pub(crate) exclude: Vec<AbsPathBuf>,
     pub(crate) is_proc_macro: bool,
+    pub(crate) repository: Option<String>,
 }
 
 impl ProjectJson {
@@ -99,6 +100,7 @@ impl ProjectJson {
                         include,
                         exclude,
                         is_proc_macro: crate_data.is_proc_macro,
+                        repository: crate_data.repository,
                     }
                 })
                 .collect::<Vec<_>>(),
@@ -142,6 +144,8 @@ struct CrateData {
     source: Option<CrateSource>,
     #[serde(default)]
     is_proc_macro: bool,
+    #[serde(default)]
+    repository: Option<String>,
 }
 
 #[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 std::{collections::VecDeque, fmt, fs, process::Command};
 
 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::env;
 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<TokenId, Id>,
     range_map: HashMap<FileRange, Id>,
     file_map: HashMap<FileId, Id>,
+    package_map: HashMap<PackageInformation, Id>,
     analysis: &'a Analysis,
     db: &'a RootDatabase,
     vfs: &'a Vfs,
@@ -57,6 +58,7 @@ impl LsifManager<'_> {
             token_map: HashMap::default(),
             range_map: HashMap::default(),
             file_map: HashMap::default(),
+            package_map: HashMap::default(),
             analysis,
             db,
             vfs,
@@ -92,6 +94,28 @@ impl LsifManager<'_> {
         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 @@ impl LsifManager<'_> {
                 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);