mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-10 23:06:23 +00:00
Merge #489
489: support std r=matklad a=matklad closes #465 Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
28559b7dd2
@ -9,6 +9,7 @@ build: &rust_build
|
||||
rust: 1.31.1
|
||||
script:
|
||||
- rustup component add rustfmt
|
||||
- rustup component add rust-src
|
||||
- cargo gen-tests --verify
|
||||
- cargo gen-syntax --verify
|
||||
- cargo test --no-run # let's measure compile time separately
|
||||
|
@ -35,12 +35,21 @@ impl fmt::Display for RawId {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Arena<ID: ArenaId, T> {
|
||||
data: Vec<T>,
|
||||
_ty: PhantomData<ID>,
|
||||
}
|
||||
|
||||
impl<ID: ArenaId, T: fmt::Debug> fmt::Debug for Arena<ID, T> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Arena")
|
||||
.field("len", &self.len())
|
||||
.field("data", &self.data)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_arena_id {
|
||||
($name:ident) => {
|
||||
|
@ -347,11 +347,11 @@ pub fn handle_runnables(
|
||||
.read()
|
||||
.file2path(ra_vfs::VfsFile(file_id.0.into()));
|
||||
let res = world.workspaces.iter().find_map(|ws| {
|
||||
let tgt = ws.target_by_root(&path)?;
|
||||
let tgt = ws.cargo.target_by_root(&path)?;
|
||||
let res = CargoTargetSpec {
|
||||
package: tgt.package(ws).name(ws).to_string(),
|
||||
target: tgt.name(ws).to_string(),
|
||||
target_kind: tgt.kind(ws),
|
||||
package: tgt.package(&ws.cargo).name(&ws.cargo).to_string(),
|
||||
target: tgt.name(&ws.cargo).to_string(),
|
||||
target_kind: tgt.kind(&ws.cargo),
|
||||
};
|
||||
Some(res)
|
||||
});
|
||||
|
@ -1,165 +1,48 @@
|
||||
mod cargo_workspace;
|
||||
mod sysroot;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use cargo_metadata::{metadata_run, CargoOpt};
|
||||
use ra_syntax::SmolStr;
|
||||
use ra_arena::{Arena, RawId, impl_arena_id};
|
||||
use rustc_hash::FxHashMap;
|
||||
use failure::{format_err, bail};
|
||||
use failure::bail;
|
||||
use thread_worker::{WorkerHandle, Worker};
|
||||
|
||||
use crate::Result;
|
||||
|
||||
/// `CargoWorksapce` represents the logical structure of, well, a Cargo
|
||||
/// workspace. It pretty closely mirrors `cargo metadata` output.
|
||||
///
|
||||
/// Note that internally, rust analyzer uses a differnet structure:
|
||||
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
|
||||
/// while this knows about `Pacakges` & `Targets`: purely cargo-related
|
||||
/// concepts.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CargoWorkspace {
|
||||
packages: Arena<Package, PackageData>,
|
||||
targets: Arena<Target, TargetData>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Package(RawId);
|
||||
impl_arena_id!(Package);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Target(RawId);
|
||||
impl_arena_id!(Target);
|
||||
pub use crate::project_model::{
|
||||
cargo_workspace::{CargoWorkspace, Package, Target, TargetKind},
|
||||
sysroot::Sysroot,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct PackageData {
|
||||
name: SmolStr,
|
||||
manifest: PathBuf,
|
||||
targets: Vec<Target>,
|
||||
is_member: bool,
|
||||
dependencies: Vec<PackageDependency>,
|
||||
pub struct ProjectWorkspace {
|
||||
pub(crate) cargo: CargoWorkspace,
|
||||
pub(crate) sysroot: Sysroot,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PackageDependency {
|
||||
pub pkg: Package,
|
||||
pub name: SmolStr,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct TargetData {
|
||||
pkg: Package,
|
||||
name: SmolStr,
|
||||
root: PathBuf,
|
||||
kind: TargetKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TargetKind {
|
||||
Bin,
|
||||
Lib,
|
||||
Example,
|
||||
Test,
|
||||
Bench,
|
||||
Other,
|
||||
}
|
||||
|
||||
impl Package {
|
||||
pub fn name(self, ws: &CargoWorkspace) -> &str {
|
||||
ws.packages[self].name.as_str()
|
||||
}
|
||||
pub fn root(self, ws: &CargoWorkspace) -> &Path {
|
||||
ws.packages[self].manifest.parent().unwrap()
|
||||
}
|
||||
pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a {
|
||||
ws.packages[self].targets.iter().cloned()
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn is_member(self, ws: &CargoWorkspace) -> bool {
|
||||
ws.packages[self].is_member
|
||||
}
|
||||
pub fn dependencies<'a>(
|
||||
self,
|
||||
ws: &'a CargoWorkspace,
|
||||
) -> impl Iterator<Item = &'a PackageDependency> + 'a {
|
||||
ws.packages[self].dependencies.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Target {
|
||||
pub fn package(self, ws: &CargoWorkspace) -> Package {
|
||||
ws.targets[self].pkg
|
||||
}
|
||||
pub fn name(self, ws: &CargoWorkspace) -> &str {
|
||||
ws.targets[self].name.as_str()
|
||||
}
|
||||
pub fn root(self, ws: &CargoWorkspace) -> &Path {
|
||||
ws.targets[self].root.as_path()
|
||||
}
|
||||
pub fn kind(self, ws: &CargoWorkspace) -> TargetKind {
|
||||
ws.targets[self].kind
|
||||
}
|
||||
}
|
||||
|
||||
impl CargoWorkspace {
|
||||
pub fn from_cargo_metadata(path: &Path) -> Result<CargoWorkspace> {
|
||||
impl ProjectWorkspace {
|
||||
pub fn discover(path: &Path) -> Result<ProjectWorkspace> {
|
||||
let cargo_toml = find_cargo_toml(path)?;
|
||||
let meta = metadata_run(
|
||||
Some(cargo_toml.as_path()),
|
||||
true,
|
||||
Some(CargoOpt::AllFeatures),
|
||||
)
|
||||
.map_err(|e| format_err!("cargo metadata failed: {}", e))?;
|
||||
let mut pkg_by_id = FxHashMap::default();
|
||||
let mut packages = Arena::default();
|
||||
let mut targets = Arena::default();
|
||||
|
||||
let ws_members = &meta.workspace_members;
|
||||
|
||||
for meta_pkg in meta.packages {
|
||||
let is_member = ws_members.contains(&meta_pkg.id);
|
||||
let pkg = packages.alloc(PackageData {
|
||||
name: meta_pkg.name.into(),
|
||||
manifest: meta_pkg.manifest_path.clone(),
|
||||
targets: Vec::new(),
|
||||
is_member,
|
||||
dependencies: Vec::new(),
|
||||
});
|
||||
let pkg_data = &mut packages[pkg];
|
||||
pkg_by_id.insert(meta_pkg.id.clone(), pkg);
|
||||
for meta_tgt in meta_pkg.targets {
|
||||
let tgt = targets.alloc(TargetData {
|
||||
pkg,
|
||||
name: meta_tgt.name.into(),
|
||||
root: meta_tgt.src_path.clone(),
|
||||
kind: TargetKind::new(meta_tgt.kind.as_slice()),
|
||||
});
|
||||
pkg_data.targets.push(tgt);
|
||||
}
|
||||
}
|
||||
let resolve = meta.resolve.expect("metadata executed with deps");
|
||||
for node in resolve.nodes {
|
||||
let source = pkg_by_id[&node.id];
|
||||
for dep_node in node.deps {
|
||||
let dep = PackageDependency {
|
||||
name: dep_node.name.into(),
|
||||
pkg: pkg_by_id[&dep_node.pkg],
|
||||
};
|
||||
packages[source].dependencies.push(dep);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CargoWorkspace { packages, targets })
|
||||
}
|
||||
pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + 'a {
|
||||
self.packages.iter().map(|(id, _pkg)| id)
|
||||
}
|
||||
pub fn target_by_root(&self, root: &Path) -> Option<Target> {
|
||||
self.packages()
|
||||
.filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root))
|
||||
.next()
|
||||
let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml)?;
|
||||
let sysroot = Sysroot::discover(&cargo_toml)?;
|
||||
let res = ProjectWorkspace { cargo, sysroot };
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn workspace_loader() -> (Worker<PathBuf, Result<ProjectWorkspace>>, WorkerHandle) {
|
||||
thread_worker::spawn::<PathBuf, Result<ProjectWorkspace>, _>(
|
||||
"workspace loader",
|
||||
1,
|
||||
|input_receiver, output_sender| {
|
||||
input_receiver
|
||||
.into_iter()
|
||||
.map(|path| ProjectWorkspace::discover(path.as_path()))
|
||||
.try_for_each(|it| output_sender.send(it))
|
||||
.unwrap()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
|
||||
if path.ends_with("Cargo.toml") {
|
||||
return Ok(path.to_path_buf());
|
||||
@ -174,33 +57,3 @@ fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
|
||||
}
|
||||
bail!("can't find Cargo.toml at {}", path.display())
|
||||
}
|
||||
|
||||
impl TargetKind {
|
||||
fn new(kinds: &[String]) -> TargetKind {
|
||||
for kind in kinds {
|
||||
return match kind.as_str() {
|
||||
"bin" => TargetKind::Bin,
|
||||
"test" => TargetKind::Test,
|
||||
"bench" => TargetKind::Bench,
|
||||
"example" => TargetKind::Example,
|
||||
_ if kind.contains("lib") => TargetKind::Lib,
|
||||
_ => continue,
|
||||
};
|
||||
}
|
||||
TargetKind::Other
|
||||
}
|
||||
}
|
||||
|
||||
pub fn workspace_loader() -> (Worker<PathBuf, Result<CargoWorkspace>>, WorkerHandle) {
|
||||
thread_worker::spawn::<PathBuf, Result<CargoWorkspace>, _>(
|
||||
"workspace loader",
|
||||
1,
|
||||
|input_receiver, output_sender| {
|
||||
input_receiver
|
||||
.into_iter()
|
||||
.map(|path| CargoWorkspace::from_cargo_metadata(path.as_path()))
|
||||
.try_for_each(|it| output_sender.send(it))
|
||||
.unwrap()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
171
crates/ra_lsp_server/src/project_model/cargo_workspace.rs
Normal file
171
crates/ra_lsp_server/src/project_model/cargo_workspace.rs
Normal file
@ -0,0 +1,171 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use cargo_metadata::{metadata_run, CargoOpt};
|
||||
use ra_syntax::SmolStr;
|
||||
use ra_arena::{Arena, RawId, impl_arena_id};
|
||||
use rustc_hash::FxHashMap;
|
||||
use failure::format_err;
|
||||
|
||||
use crate::Result;
|
||||
|
||||
/// `CargoWorksapce` represents the logical structure of, well, a Cargo
|
||||
/// workspace. It pretty closely mirrors `cargo metadata` output.
|
||||
///
|
||||
/// Note that internally, rust analyzer uses a differnet structure:
|
||||
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
|
||||
/// while this knows about `Pacakges` & `Targets`: purely cargo-related
|
||||
/// concepts.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CargoWorkspace {
|
||||
packages: Arena<Package, PackageData>,
|
||||
targets: Arena<Target, TargetData>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Package(RawId);
|
||||
impl_arena_id!(Package);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Target(RawId);
|
||||
impl_arena_id!(Target);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct PackageData {
|
||||
name: SmolStr,
|
||||
manifest: PathBuf,
|
||||
targets: Vec<Target>,
|
||||
is_member: bool,
|
||||
dependencies: Vec<PackageDependency>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PackageDependency {
|
||||
pub pkg: Package,
|
||||
pub name: SmolStr,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct TargetData {
|
||||
pkg: Package,
|
||||
name: SmolStr,
|
||||
root: PathBuf,
|
||||
kind: TargetKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TargetKind {
|
||||
Bin,
|
||||
Lib,
|
||||
Example,
|
||||
Test,
|
||||
Bench,
|
||||
Other,
|
||||
}
|
||||
|
||||
impl TargetKind {
|
||||
fn new(kinds: &[String]) -> TargetKind {
|
||||
for kind in kinds {
|
||||
return match kind.as_str() {
|
||||
"bin" => TargetKind::Bin,
|
||||
"test" => TargetKind::Test,
|
||||
"bench" => TargetKind::Bench,
|
||||
"example" => TargetKind::Example,
|
||||
_ if kind.contains("lib") => TargetKind::Lib,
|
||||
_ => continue,
|
||||
};
|
||||
}
|
||||
TargetKind::Other
|
||||
}
|
||||
}
|
||||
|
||||
impl Package {
|
||||
pub fn name(self, ws: &CargoWorkspace) -> &str {
|
||||
ws.packages[self].name.as_str()
|
||||
}
|
||||
pub fn root(self, ws: &CargoWorkspace) -> &Path {
|
||||
ws.packages[self].manifest.parent().unwrap()
|
||||
}
|
||||
pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a {
|
||||
ws.packages[self].targets.iter().cloned()
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn is_member(self, ws: &CargoWorkspace) -> bool {
|
||||
ws.packages[self].is_member
|
||||
}
|
||||
pub fn dependencies<'a>(
|
||||
self,
|
||||
ws: &'a CargoWorkspace,
|
||||
) -> impl Iterator<Item = &'a PackageDependency> + 'a {
|
||||
ws.packages[self].dependencies.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Target {
|
||||
pub fn package(self, ws: &CargoWorkspace) -> Package {
|
||||
ws.targets[self].pkg
|
||||
}
|
||||
pub fn name(self, ws: &CargoWorkspace) -> &str {
|
||||
ws.targets[self].name.as_str()
|
||||
}
|
||||
pub fn root(self, ws: &CargoWorkspace) -> &Path {
|
||||
ws.targets[self].root.as_path()
|
||||
}
|
||||
pub fn kind(self, ws: &CargoWorkspace) -> TargetKind {
|
||||
ws.targets[self].kind
|
||||
}
|
||||
}
|
||||
|
||||
impl CargoWorkspace {
|
||||
pub fn from_cargo_metadata(cargo_toml: &Path) -> Result<CargoWorkspace> {
|
||||
let meta = metadata_run(Some(cargo_toml), true, Some(CargoOpt::AllFeatures))
|
||||
.map_err(|e| format_err!("cargo metadata failed: {}", e))?;
|
||||
let mut pkg_by_id = FxHashMap::default();
|
||||
let mut packages = Arena::default();
|
||||
let mut targets = Arena::default();
|
||||
|
||||
let ws_members = &meta.workspace_members;
|
||||
|
||||
for meta_pkg in meta.packages {
|
||||
let is_member = ws_members.contains(&meta_pkg.id);
|
||||
let pkg = packages.alloc(PackageData {
|
||||
name: meta_pkg.name.into(),
|
||||
manifest: meta_pkg.manifest_path.clone(),
|
||||
targets: Vec::new(),
|
||||
is_member,
|
||||
dependencies: Vec::new(),
|
||||
});
|
||||
let pkg_data = &mut packages[pkg];
|
||||
pkg_by_id.insert(meta_pkg.id.clone(), pkg);
|
||||
for meta_tgt in meta_pkg.targets {
|
||||
let tgt = targets.alloc(TargetData {
|
||||
pkg,
|
||||
name: meta_tgt.name.into(),
|
||||
root: meta_tgt.src_path.clone(),
|
||||
kind: TargetKind::new(meta_tgt.kind.as_slice()),
|
||||
});
|
||||
pkg_data.targets.push(tgt);
|
||||
}
|
||||
}
|
||||
let resolve = meta.resolve.expect("metadata executed with deps");
|
||||
for node in resolve.nodes {
|
||||
let source = pkg_by_id[&node.id];
|
||||
for dep_node in node.deps {
|
||||
let dep = PackageDependency {
|
||||
name: dep_node.name.into(),
|
||||
pkg: pkg_by_id[&dep_node.pkg],
|
||||
};
|
||||
packages[source].dependencies.push(dep);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CargoWorkspace { packages, targets })
|
||||
}
|
||||
pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + 'a {
|
||||
self.packages.iter().map(|(id, _pkg)| id)
|
||||
}
|
||||
pub fn target_by_root(&self, root: &Path) -> Option<Target> {
|
||||
self.packages()
|
||||
.filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root))
|
||||
.next()
|
||||
}
|
||||
}
|
132
crates/ra_lsp_server/src/project_model/sysroot.rs
Normal file
132
crates/ra_lsp_server/src/project_model/sysroot.rs
Normal file
@ -0,0 +1,132 @@
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use ra_syntax::SmolStr;
|
||||
use ra_arena::{Arena, RawId, impl_arena_id};
|
||||
|
||||
use crate::Result;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Sysroot {
|
||||
crates: Arena<SysrootCrate, SysrootCrateData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct SysrootCrate(RawId);
|
||||
impl_arena_id!(SysrootCrate);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct SysrootCrateData {
|
||||
name: SmolStr,
|
||||
root: PathBuf,
|
||||
deps: Vec<SysrootCrate>,
|
||||
}
|
||||
|
||||
impl Sysroot {
|
||||
pub(crate) fn std(&self) -> Option<SysrootCrate> {
|
||||
self.by_name("std")
|
||||
}
|
||||
|
||||
pub(crate) fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + 'a {
|
||||
self.crates.iter().map(|(id, _data)| id)
|
||||
}
|
||||
|
||||
pub(super) fn discover(cargo_toml: &Path) -> Result<Sysroot> {
|
||||
let rustc_output = Command::new("rustc")
|
||||
.current_dir(cargo_toml.parent().unwrap())
|
||||
.args(&["--print", "sysroot"])
|
||||
.output()?;
|
||||
if !rustc_output.status.success() {
|
||||
failure::bail!("failed to locate sysroot")
|
||||
}
|
||||
let stdout = String::from_utf8(rustc_output.stdout)?;
|
||||
let sysroot_path = Path::new(stdout.trim());
|
||||
let src = sysroot_path.join("lib/rustlib/src/rust/src");
|
||||
|
||||
let mut sysroot = Sysroot {
|
||||
crates: Arena::default(),
|
||||
};
|
||||
for name in SYSROOT_CRATES.trim().lines() {
|
||||
let root = src.join(format!("lib{}", name)).join("lib.rs");
|
||||
if root.exists() {
|
||||
sysroot.crates.alloc(SysrootCrateData {
|
||||
name: name.into(),
|
||||
root,
|
||||
deps: Vec::new(),
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(std) = sysroot.std() {
|
||||
for dep in STD_DEPS.trim().lines() {
|
||||
if let Some(dep) = sysroot.by_name(dep) {
|
||||
sysroot.crates[std].deps.push(dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(sysroot)
|
||||
}
|
||||
|
||||
fn by_name(&self, name: &str) -> Option<SysrootCrate> {
|
||||
self.crates
|
||||
.iter()
|
||||
.find(|(_id, data)| data.name == name)
|
||||
.map(|(id, _data)| id)
|
||||
}
|
||||
}
|
||||
|
||||
impl SysrootCrate {
|
||||
pub(crate) fn name(self, sysroot: &Sysroot) -> &SmolStr {
|
||||
&sysroot.crates[self].name
|
||||
}
|
||||
pub(crate) fn root(self, sysroot: &Sysroot) -> &Path {
|
||||
sysroot.crates[self].root.as_path()
|
||||
}
|
||||
pub(crate) fn root_dir(self, sysroot: &Sysroot) -> &Path {
|
||||
self.root(sysroot).parent().unwrap()
|
||||
}
|
||||
pub(crate) fn deps<'a>(self, sysroot: &'a Sysroot) -> impl Iterator<Item = SysrootCrate> + 'a {
|
||||
sysroot.crates[self].deps.iter().map(|&it| it)
|
||||
}
|
||||
}
|
||||
|
||||
const SYSROOT_CRATES: &str = "
|
||||
std
|
||||
core
|
||||
alloc
|
||||
collections
|
||||
libc
|
||||
panic_unwind
|
||||
proc_macro
|
||||
rustc_unicode
|
||||
std_unicode
|
||||
test
|
||||
alloc_jemalloc
|
||||
alloc_system
|
||||
compiler_builtins
|
||||
getopts
|
||||
panic_unwind
|
||||
panic_abort
|
||||
rand
|
||||
term
|
||||
unwind
|
||||
build_helper
|
||||
rustc_asan
|
||||
rustc_lsan
|
||||
rustc_msan
|
||||
rustc_tsan
|
||||
syntax";
|
||||
|
||||
const STD_DEPS: &str = "
|
||||
alloc_jemalloc
|
||||
alloc_system
|
||||
panic_abort
|
||||
rand
|
||||
compiler_builtins
|
||||
unwind
|
||||
rustc_asan
|
||||
rustc_lsan
|
||||
rustc_msan
|
||||
rustc_tsan
|
||||
build_helper";
|
@ -15,7 +15,7 @@ use parking_lot::RwLock;
|
||||
use failure::format_err;
|
||||
|
||||
use crate::{
|
||||
project_model::{CargoWorkspace, TargetKind},
|
||||
project_model::{ProjectWorkspace, TargetKind},
|
||||
Result,
|
||||
};
|
||||
|
||||
@ -23,28 +23,33 @@ use crate::{
|
||||
pub struct ServerWorldState {
|
||||
pub roots_to_scan: usize,
|
||||
pub root: PathBuf,
|
||||
pub workspaces: Arc<Vec<CargoWorkspace>>,
|
||||
pub workspaces: Arc<Vec<ProjectWorkspace>>,
|
||||
pub analysis_host: AnalysisHost,
|
||||
pub vfs: Arc<RwLock<Vfs>>,
|
||||
}
|
||||
|
||||
pub struct ServerWorld {
|
||||
pub workspaces: Arc<Vec<CargoWorkspace>>,
|
||||
pub workspaces: Arc<Vec<ProjectWorkspace>>,
|
||||
pub analysis: Analysis,
|
||||
pub vfs: Arc<RwLock<Vfs>>,
|
||||
}
|
||||
|
||||
impl ServerWorldState {
|
||||
pub fn new(root: PathBuf, workspaces: Vec<CargoWorkspace>) -> ServerWorldState {
|
||||
pub fn new(root: PathBuf, workspaces: Vec<ProjectWorkspace>) -> ServerWorldState {
|
||||
let mut change = AnalysisChange::new();
|
||||
|
||||
let mut roots = Vec::new();
|
||||
roots.push(root.clone());
|
||||
for ws in workspaces.iter() {
|
||||
for pkg in ws.packages() {
|
||||
roots.push(pkg.root(&ws).to_path_buf());
|
||||
for pkg in ws.cargo.packages() {
|
||||
roots.push(pkg.root(&ws.cargo).to_path_buf());
|
||||
}
|
||||
for krate in ws.sysroot.crates() {
|
||||
roots.push(krate.root_dir(&ws.sysroot).to_path_buf())
|
||||
}
|
||||
}
|
||||
roots.sort();
|
||||
roots.dedup();
|
||||
let roots_to_scan = roots.len();
|
||||
let (mut vfs, roots) = Vfs::new(roots);
|
||||
for r in roots {
|
||||
@ -53,16 +58,43 @@ impl ServerWorldState {
|
||||
}
|
||||
|
||||
let mut crate_graph = CrateGraph::default();
|
||||
let mut pkg_to_lib_crate = FxHashMap::default();
|
||||
let mut pkg_crates = FxHashMap::default();
|
||||
for ws in workspaces.iter() {
|
||||
for pkg in ws.packages() {
|
||||
for tgt in pkg.targets(ws) {
|
||||
let root = tgt.root(ws);
|
||||
// First, load std
|
||||
let mut sysroot_crates = FxHashMap::default();
|
||||
for krate in ws.sysroot.crates() {
|
||||
if let Some(file_id) = vfs.load(krate.root(&ws.sysroot)) {
|
||||
let file_id = FileId(file_id.0.into());
|
||||
sysroot_crates.insert(krate, crate_graph.add_crate_root(file_id));
|
||||
}
|
||||
}
|
||||
for from in ws.sysroot.crates() {
|
||||
for to in from.deps(&ws.sysroot) {
|
||||
let name = to.name(&ws.sysroot);
|
||||
if let (Some(&from), Some(&to)) =
|
||||
(sysroot_crates.get(&from), sysroot_crates.get(&to))
|
||||
{
|
||||
crate_graph.add_dep(from, name.clone(), to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let libstd = ws
|
||||
.sysroot
|
||||
.std()
|
||||
.and_then(|it| sysroot_crates.get(&it).map(|&it| it));
|
||||
|
||||
let mut pkg_to_lib_crate = FxHashMap::default();
|
||||
let mut pkg_crates = FxHashMap::default();
|
||||
// Next, create crates for each package, target pair
|
||||
for pkg in ws.cargo.packages() {
|
||||
let mut lib_tgt = None;
|
||||
for tgt in pkg.targets(&ws.cargo) {
|
||||
let root = tgt.root(&ws.cargo);
|
||||
if let Some(file_id) = vfs.load(root) {
|
||||
let file_id = FileId(file_id.0.into());
|
||||
let crate_id = crate_graph.add_crate_root(file_id);
|
||||
if tgt.kind(ws) == TargetKind::Lib {
|
||||
if tgt.kind(&ws.cargo) == TargetKind::Lib {
|
||||
lib_tgt = Some(crate_id);
|
||||
pkg_to_lib_crate.insert(pkg, crate_id);
|
||||
}
|
||||
pkg_crates
|
||||
@ -71,9 +103,24 @@ impl ServerWorldState {
|
||||
.push(crate_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Set deps to the std and to the lib target of the current package
|
||||
for &from in pkg_crates.get(&pkg).into_iter().flatten() {
|
||||
if let Some(to) = lib_tgt {
|
||||
if to != from {
|
||||
crate_graph.add_dep(from, pkg.name(&ws.cargo).into(), to);
|
||||
}
|
||||
}
|
||||
if let Some(std) = libstd {
|
||||
crate_graph.add_dep(from, "std".into(), std);
|
||||
}
|
||||
}
|
||||
}
|
||||
for pkg in ws.packages() {
|
||||
for dep in pkg.dependencies(ws) {
|
||||
|
||||
// Now add a dep ednge from all targets of upstream to the lib
|
||||
// target of downstream.
|
||||
for pkg in ws.cargo.packages() {
|
||||
for dep in pkg.dependencies(&ws.cargo) {
|
||||
if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
|
||||
for &from in pkg_crates.get(&pkg).into_iter().flatten() {
|
||||
crate_graph.add_dep(from, dep.name.clone(), to);
|
||||
|
@ -1,10 +1,15 @@
|
||||
mod support;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use languageserver_types::{
|
||||
CodeActionContext, DocumentFormattingParams, FormattingOptions, Position, Range,
|
||||
};
|
||||
use ra_lsp_server::req::{
|
||||
CodeActionParams, CodeActionRequest, Formatting, Runnables, RunnablesParams,
|
||||
CodeActionParams, CodeActionRequest, Formatting, Runnables, RunnablesParams, CompletionParams, Completion,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
@ -12,6 +17,32 @@ use crate::support::project;
|
||||
|
||||
const LOG: &'static str = "";
|
||||
|
||||
#[test]
|
||||
fn completes_items_from_standard_library() {
|
||||
let project_start = Instant::now();
|
||||
let server = project(
|
||||
r#"
|
||||
//- Cargo.toml
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.0.0"
|
||||
|
||||
//- src/lib.rs
|
||||
use std::collections::Spam;
|
||||
"#,
|
||||
);
|
||||
server.wait_for_feedback("workspace loaded");
|
||||
eprintln!("loading took {:?}", project_start.elapsed());
|
||||
let completion_start = Instant::now();
|
||||
let res = server.send_request::<Completion>(CompletionParams {
|
||||
text_document: server.doc_id("src/lib.rs"),
|
||||
context: None,
|
||||
position: Position::new(0, 23),
|
||||
});
|
||||
assert!(format!("{}", res).contains("HashMap"));
|
||||
eprintln!("completion took {:?}", completion_start.elapsed());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_runnables_no_project() {
|
||||
let server = project(
|
||||
@ -122,7 +153,6 @@ fn test_eggs() {}
|
||||
);
|
||||
}
|
||||
|
||||
use std::collections::HashMap;
|
||||
#[test]
|
||||
fn test_format_document() {
|
||||
let server = project(
|
||||
|
@ -93,9 +93,7 @@ impl Server {
|
||||
R: Request,
|
||||
R::Params: Serialize,
|
||||
{
|
||||
let id = self.req_id.get();
|
||||
self.req_id.set(id + 1);
|
||||
let actual = self.send_request::<R>(id, params);
|
||||
let actual = self.send_request::<R>(params);
|
||||
match find_mismatch(&expected_resp, &actual) {
|
||||
Some((expected_part, actual_part)) => panic!(
|
||||
"JSON mismatch\nExpected:\n{}\nWas:\n{}\nExpected part:\n{}\nActual part:\n{}\n",
|
||||
@ -108,11 +106,14 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
fn send_request<R>(&self, id: u64, params: R::Params) -> Value
|
||||
pub fn send_request<R>(&self, params: R::Params) -> Value
|
||||
where
|
||||
R: Request,
|
||||
R::Params: Serialize,
|
||||
{
|
||||
let id = self.req_id.get();
|
||||
self.req_id.set(id + 1);
|
||||
|
||||
let r = RawRequest::new::<R>(id, ¶ms);
|
||||
self.send_request_(r)
|
||||
}
|
||||
@ -178,7 +179,7 @@ impl Server {
|
||||
|
||||
impl Drop for Server {
|
||||
fn drop(&mut self) {
|
||||
self.send_request::<Shutdown>(666, ());
|
||||
self.send_request::<Shutdown>(());
|
||||
let receiver = self.worker.take().unwrap().shutdown();
|
||||
while let Some(msg) = recv_timeout(&receiver) {
|
||||
drop(msg);
|
||||
@ -188,7 +189,7 @@ impl Drop for Server {
|
||||
}
|
||||
|
||||
fn recv_timeout(receiver: &Receiver<RawMessage>) -> Option<RawMessage> {
|
||||
let timeout = Duration::from_secs(5);
|
||||
let timeout = Duration::from_secs(50);
|
||||
select! {
|
||||
recv(receiver) -> msg => msg.ok(),
|
||||
recv(after(timeout)) -> _ => panic!("timed out"),
|
||||
|
Loading…
Reference in New Issue
Block a user