mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-12 05:08:52 +00:00
Be more explicit about absolute paths at various places
This commit is contained in:
parent
dff62def2e
commit
154cb8243b
11 changed files with 126 additions and 94 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1174,6 +1174,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cargo_metadata",
|
"cargo_metadata",
|
||||||
"log",
|
"log",
|
||||||
|
"paths",
|
||||||
"ra_arena",
|
"ra_arena",
|
||||||
"ra_cfg",
|
"ra_cfg",
|
||||||
"ra_db",
|
"ra_db",
|
||||||
|
|
|
@ -28,6 +28,12 @@ impl AsRef<Path> for AbsPathBuf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRef<AbsPath> for AbsPathBuf {
|
||||||
|
fn as_ref(&self) -> &AbsPath {
|
||||||
|
self.as_path()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<PathBuf> for AbsPathBuf {
|
impl TryFrom<PathBuf> for AbsPathBuf {
|
||||||
type Error = PathBuf;
|
type Error = PathBuf;
|
||||||
fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
|
fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
|
||||||
|
@ -45,9 +51,19 @@ impl TryFrom<&str> for AbsPathBuf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq<AbsPath> for AbsPathBuf {
|
||||||
|
fn eq(&self, other: &AbsPath) -> bool {
|
||||||
|
self.as_path() == other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AbsPathBuf {
|
impl AbsPathBuf {
|
||||||
|
pub fn assert(path: PathBuf) -> AbsPathBuf {
|
||||||
|
AbsPathBuf::try_from(path)
|
||||||
|
.unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display()))
|
||||||
|
}
|
||||||
pub fn as_path(&self) -> &AbsPath {
|
pub fn as_path(&self) -> &AbsPath {
|
||||||
AbsPath::new_unchecked(self.0.as_path())
|
AbsPath::assert(self.0.as_path())
|
||||||
}
|
}
|
||||||
pub fn pop(&mut self) -> bool {
|
pub fn pop(&mut self) -> bool {
|
||||||
self.0.pop()
|
self.0.pop()
|
||||||
|
@ -77,15 +93,19 @@ impl<'a> TryFrom<&'a Path> for &'a AbsPath {
|
||||||
if !path.is_absolute() {
|
if !path.is_absolute() {
|
||||||
return Err(path);
|
return Err(path);
|
||||||
}
|
}
|
||||||
Ok(AbsPath::new_unchecked(path))
|
Ok(AbsPath::assert(path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbsPath {
|
impl AbsPath {
|
||||||
fn new_unchecked(path: &Path) -> &AbsPath {
|
pub fn assert(path: &Path) -> &AbsPath {
|
||||||
|
assert!(path.is_absolute());
|
||||||
unsafe { &*(path as *const Path as *const AbsPath) }
|
unsafe { &*(path as *const Path as *const AbsPath) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parent(&self) -> Option<&AbsPath> {
|
||||||
|
self.0.parent().map(AbsPath::assert)
|
||||||
|
}
|
||||||
pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
|
pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
|
||||||
self.as_ref().join(path).try_into().unwrap()
|
self.as_ref().join(path).try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ ra_cfg = { path = "../ra_cfg" }
|
||||||
ra_db = { path = "../ra_db" }
|
ra_db = { path = "../ra_db" }
|
||||||
ra_toolchain = { path = "../ra_toolchain" }
|
ra_toolchain = { path = "../ra_toolchain" }
|
||||||
ra_proc_macro = { path = "../ra_proc_macro" }
|
ra_proc_macro = { path = "../ra_proc_macro" }
|
||||||
|
paths = { path = "../paths" }
|
||||||
|
|
||||||
serde = { version = "1.0.106", features = ["derive"] }
|
serde = { version = "1.0.106", features = ["derive"] }
|
||||||
serde_json = "1.0.48"
|
serde_json = "1.0.48"
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
use std::{
|
use std::{ffi::OsStr, ops, path::Path, process::Command};
|
||||||
ffi::OsStr,
|
|
||||||
ops,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
process::Command,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
|
use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
|
||||||
|
use paths::{AbsPath, AbsPathBuf};
|
||||||
use ra_arena::{Arena, Idx};
|
use ra_arena::{Arena, Idx};
|
||||||
use ra_db::Edition;
|
use ra_db::Edition;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
@ -20,11 +16,14 @@ use rustc_hash::FxHashMap;
|
||||||
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
|
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
|
||||||
/// while this knows about `Packages` & `Targets`: purely cargo-related
|
/// while this knows about `Packages` & `Targets`: purely cargo-related
|
||||||
/// concepts.
|
/// concepts.
|
||||||
|
///
|
||||||
|
/// We use absolute paths here, `cargo metadata` guarantees to always produce
|
||||||
|
/// abs paths.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CargoWorkspace {
|
pub struct CargoWorkspace {
|
||||||
packages: Arena<PackageData>,
|
packages: Arena<PackageData>,
|
||||||
targets: Arena<TargetData>,
|
targets: Arena<TargetData>,
|
||||||
workspace_root: PathBuf,
|
workspace_root: AbsPathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Index<Package> for CargoWorkspace {
|
impl ops::Index<Package> for CargoWorkspace {
|
||||||
|
@ -80,15 +79,15 @@ pub type Target = Idx<TargetData>;
|
||||||
pub struct PackageData {
|
pub struct PackageData {
|
||||||
pub version: String,
|
pub version: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub manifest: PathBuf,
|
pub manifest: AbsPathBuf,
|
||||||
pub targets: Vec<Target>,
|
pub targets: Vec<Target>,
|
||||||
pub is_member: bool,
|
pub is_member: bool,
|
||||||
pub dependencies: Vec<PackageDependency>,
|
pub dependencies: Vec<PackageDependency>,
|
||||||
pub edition: Edition,
|
pub edition: Edition,
|
||||||
pub features: Vec<String>,
|
pub features: Vec<String>,
|
||||||
pub cfgs: Vec<String>,
|
pub cfgs: Vec<String>,
|
||||||
pub out_dir: Option<PathBuf>,
|
pub out_dir: Option<AbsPathBuf>,
|
||||||
pub proc_macro_dylib_path: Option<PathBuf>,
|
pub proc_macro_dylib_path: Option<AbsPathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -101,7 +100,7 @@ pub struct PackageDependency {
|
||||||
pub struct TargetData {
|
pub struct TargetData {
|
||||||
pub package: Package,
|
pub package: Package,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub root: PathBuf,
|
pub root: AbsPathBuf,
|
||||||
pub kind: TargetKind,
|
pub kind: TargetKind,
|
||||||
pub is_proc_macro: bool,
|
pub is_proc_macro: bool,
|
||||||
}
|
}
|
||||||
|
@ -135,7 +134,7 @@ impl TargetKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackageData {
|
impl PackageData {
|
||||||
pub fn root(&self) -> &Path {
|
pub fn root(&self) -> &AbsPath {
|
||||||
self.manifest.parent().unwrap()
|
self.manifest.parent().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,7 +192,7 @@ impl CargoWorkspace {
|
||||||
let pkg = packages.alloc(PackageData {
|
let pkg = packages.alloc(PackageData {
|
||||||
name,
|
name,
|
||||||
version: version.to_string(),
|
version: version.to_string(),
|
||||||
manifest: manifest_path,
|
manifest: AbsPathBuf::assert(manifest_path),
|
||||||
targets: Vec::new(),
|
targets: Vec::new(),
|
||||||
is_member,
|
is_member,
|
||||||
edition,
|
edition,
|
||||||
|
@ -210,7 +209,7 @@ impl CargoWorkspace {
|
||||||
let tgt = targets.alloc(TargetData {
|
let tgt = targets.alloc(TargetData {
|
||||||
package: pkg,
|
package: pkg,
|
||||||
name: meta_tgt.name,
|
name: meta_tgt.name,
|
||||||
root: meta_tgt.src_path.clone(),
|
root: AbsPathBuf::assert(meta_tgt.src_path.clone()),
|
||||||
kind: TargetKind::new(meta_tgt.kind.as_slice()),
|
kind: TargetKind::new(meta_tgt.kind.as_slice()),
|
||||||
is_proc_macro,
|
is_proc_macro,
|
||||||
});
|
});
|
||||||
|
@ -246,16 +245,17 @@ impl CargoWorkspace {
|
||||||
packages[source].features.extend(node.features);
|
packages[source].features.extend(node.features);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root })
|
let workspace_root = AbsPathBuf::assert(meta.workspace_root);
|
||||||
|
Ok(CargoWorkspace { packages, targets, workspace_root: workspace_root })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
|
pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
|
||||||
self.packages.iter().map(|(id, _pkg)| id)
|
self.packages.iter().map(|(id, _pkg)| id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn target_by_root(&self, root: &Path) -> Option<Target> {
|
pub fn target_by_root(&self, root: &AbsPath) -> Option<Target> {
|
||||||
self.packages()
|
self.packages()
|
||||||
.filter_map(|pkg| self[pkg].targets.iter().find(|&&it| self[it].root == root))
|
.filter_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root))
|
||||||
.next()
|
.next()
|
||||||
.copied()
|
.copied()
|
||||||
}
|
}
|
||||||
|
@ -279,8 +279,8 @@ impl CargoWorkspace {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ExternResources {
|
pub struct ExternResources {
|
||||||
out_dirs: FxHashMap<PackageId, PathBuf>,
|
out_dirs: FxHashMap<PackageId, AbsPathBuf>,
|
||||||
proc_dylib_paths: FxHashMap<PackageId, PathBuf>,
|
proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>,
|
||||||
cfgs: FxHashMap<PackageId, Vec<String>>,
|
cfgs: FxHashMap<PackageId, Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,6 +308,7 @@ pub fn load_extern_resources(
|
||||||
if let Ok(message) = message {
|
if let Ok(message) = message {
|
||||||
match message {
|
match message {
|
||||||
Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => {
|
Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => {
|
||||||
|
let out_dir = AbsPathBuf::assert(out_dir);
|
||||||
res.out_dirs.insert(package_id.clone(), out_dir);
|
res.out_dirs.insert(package_id.clone(), out_dir);
|
||||||
res.cfgs.insert(package_id, cfgs);
|
res.cfgs.insert(package_id, cfgs);
|
||||||
}
|
}
|
||||||
|
@ -317,7 +318,8 @@ pub fn load_extern_resources(
|
||||||
// Skip rmeta file
|
// Skip rmeta file
|
||||||
if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
|
if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
|
||||||
{
|
{
|
||||||
res.proc_dylib_paths.insert(package_id, filename.clone());
|
let filename = AbsPathBuf::assert(filename.clone());
|
||||||
|
res.proc_dylib_paths.insert(package_id, filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,12 @@ mod sysroot;
|
||||||
use std::{
|
use std::{
|
||||||
fs::{read_dir, File, ReadDir},
|
fs::{read_dir, File, ReadDir},
|
||||||
io::{self, BufReader},
|
io::{self, BufReader},
|
||||||
path::{Path, PathBuf},
|
path::Path,
|
||||||
process::{Command, Output},
|
process::{Command, Output},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
|
use paths::{AbsPath, AbsPathBuf};
|
||||||
use ra_cfg::CfgOptions;
|
use ra_cfg::CfgOptions;
|
||||||
use ra_db::{CrateGraph, CrateName, Edition, Env, FileId};
|
use ra_db::{CrateGraph, CrateName, Edition, Env, FileId};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
@ -29,7 +30,7 @@ pub enum ProjectWorkspace {
|
||||||
/// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
|
/// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
|
||||||
Cargo { cargo: CargoWorkspace, sysroot: Sysroot },
|
Cargo { cargo: CargoWorkspace, sysroot: Sysroot },
|
||||||
/// Project workspace was manually specified using a `rust-project.json` file.
|
/// Project workspace was manually specified using a `rust-project.json` file.
|
||||||
Json { project: JsonProject, project_location: PathBuf },
|
Json { project: JsonProject, project_location: AbsPathBuf },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `PackageRoot` describes a package root folder.
|
/// `PackageRoot` describes a package root folder.
|
||||||
|
@ -38,22 +39,22 @@ pub enum ProjectWorkspace {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PackageRoot {
|
pub struct PackageRoot {
|
||||||
/// Path to the root folder
|
/// Path to the root folder
|
||||||
path: PathBuf,
|
path: AbsPathBuf,
|
||||||
/// Is a member of the current workspace
|
/// Is a member of the current workspace
|
||||||
is_member: bool,
|
is_member: bool,
|
||||||
out_dir: Option<PathBuf>,
|
out_dir: Option<AbsPathBuf>,
|
||||||
}
|
}
|
||||||
impl PackageRoot {
|
impl PackageRoot {
|
||||||
pub fn new_member(path: PathBuf) -> PackageRoot {
|
pub fn new_member(path: AbsPathBuf) -> PackageRoot {
|
||||||
Self { path, is_member: true, out_dir: None }
|
Self { path, is_member: true, out_dir: None }
|
||||||
}
|
}
|
||||||
pub fn new_non_member(path: PathBuf) -> PackageRoot {
|
pub fn new_non_member(path: AbsPathBuf) -> PackageRoot {
|
||||||
Self { path, is_member: false, out_dir: None }
|
Self { path, is_member: false, out_dir: None }
|
||||||
}
|
}
|
||||||
pub fn path(&self) -> &Path {
|
pub fn path(&self) -> &AbsPath {
|
||||||
&self.path
|
&self.path
|
||||||
}
|
}
|
||||||
pub fn out_dir(&self) -> Option<&Path> {
|
pub fn out_dir(&self) -> Option<&AbsPath> {
|
||||||
self.out_dir.as_deref()
|
self.out_dir.as_deref()
|
||||||
}
|
}
|
||||||
pub fn is_member(&self) -> bool {
|
pub fn is_member(&self) -> bool {
|
||||||
|
@ -63,12 +64,12 @@ impl PackageRoot {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
pub enum ProjectManifest {
|
pub enum ProjectManifest {
|
||||||
ProjectJson(PathBuf),
|
ProjectJson(AbsPathBuf),
|
||||||
CargoToml(PathBuf),
|
CargoToml(AbsPathBuf),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProjectManifest {
|
impl ProjectManifest {
|
||||||
pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> {
|
pub fn from_manifest_file(path: AbsPathBuf) -> Result<ProjectManifest> {
|
||||||
if path.ends_with("rust-project.json") {
|
if path.ends_with("rust-project.json") {
|
||||||
return Ok(ProjectManifest::ProjectJson(path));
|
return Ok(ProjectManifest::ProjectJson(path));
|
||||||
}
|
}
|
||||||
|
@ -78,7 +79,7 @@ impl ProjectManifest {
|
||||||
bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
|
bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn discover_single(path: &Path) -> Result<ProjectManifest> {
|
pub fn discover_single(path: &AbsPath) -> Result<ProjectManifest> {
|
||||||
let mut candidates = ProjectManifest::discover(path)?;
|
let mut candidates = ProjectManifest::discover(path)?;
|
||||||
let res = match candidates.pop() {
|
let res = match candidates.pop() {
|
||||||
None => bail!("no projects"),
|
None => bail!("no projects"),
|
||||||
|
@ -91,23 +92,23 @@ impl ProjectManifest {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> {
|
pub fn discover(path: &AbsPath) -> io::Result<Vec<ProjectManifest>> {
|
||||||
if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
|
if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
|
||||||
return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
|
return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
|
||||||
}
|
}
|
||||||
return find_cargo_toml(path)
|
return find_cargo_toml(path)
|
||||||
.map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());
|
.map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());
|
||||||
|
|
||||||
fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
|
fn find_cargo_toml(path: &AbsPath) -> io::Result<Vec<AbsPathBuf>> {
|
||||||
match find_in_parent_dirs(path, "Cargo.toml") {
|
match find_in_parent_dirs(path, "Cargo.toml") {
|
||||||
Some(it) => Ok(vec![it]),
|
Some(it) => Ok(vec![it]),
|
||||||
None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)),
|
None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_in_parent_dirs(path: &Path, target_file_name: &str) -> Option<PathBuf> {
|
fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option<AbsPathBuf> {
|
||||||
if path.ends_with(target_file_name) {
|
if path.ends_with(target_file_name) {
|
||||||
return Some(path.to_owned());
|
return Some(path.to_path_buf());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut curr = Some(path);
|
let mut curr = Some(path);
|
||||||
|
@ -123,17 +124,18 @@ impl ProjectManifest {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> {
|
fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<AbsPathBuf> {
|
||||||
// Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
|
// Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
|
||||||
entities
|
entities
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.map(|it| it.path().join("Cargo.toml"))
|
.map(|it| it.path().join("Cargo.toml"))
|
||||||
.filter(|it| it.exists())
|
.filter(|it| it.exists())
|
||||||
|
.map(AbsPathBuf::assert)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn discover_all(paths: &[impl AsRef<Path>]) -> Vec<ProjectManifest> {
|
pub fn discover_all(paths: &[impl AsRef<AbsPath>]) -> Vec<ProjectManifest> {
|
||||||
let mut res = paths
|
let mut res = paths
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
|
.filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
|
||||||
|
@ -158,15 +160,12 @@ impl ProjectWorkspace {
|
||||||
format!("Failed to open json file {}", project_json.display())
|
format!("Failed to open json file {}", project_json.display())
|
||||||
})?;
|
})?;
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
let project_location = match project_json.parent() {
|
let project_location = project_json.parent().unwrap().to_path_buf();
|
||||||
Some(parent) => PathBuf::from(parent),
|
|
||||||
None => PathBuf::new(),
|
|
||||||
};
|
|
||||||
ProjectWorkspace::Json {
|
ProjectWorkspace::Json {
|
||||||
project: from_reader(reader).with_context(|| {
|
project: from_reader(reader).with_context(|| {
|
||||||
format!("Failed to deserialize json file {}", project_json.display())
|
format!("Failed to deserialize json file {}", project_json.display())
|
||||||
})?,
|
})?,
|
||||||
project_location: project_location,
|
project_location,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ProjectManifest::CargoToml(cargo_toml) => {
|
ProjectManifest::CargoToml(cargo_toml) => {
|
||||||
|
@ -218,13 +217,13 @@ impl ProjectWorkspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> {
|
pub fn proc_macro_dylib_paths(&self) -> Vec<AbsPathBuf> {
|
||||||
match self {
|
match self {
|
||||||
ProjectWorkspace::Json { project, .. } => project
|
ProjectWorkspace::Json { project, project_location } => project
|
||||||
.crates
|
.crates
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|krate| krate.proc_macro_dylib_path.as_ref())
|
.filter_map(|krate| krate.proc_macro_dylib_path.as_ref())
|
||||||
.cloned()
|
.map(|it| project_location.join(it))
|
||||||
.collect(),
|
.collect(),
|
||||||
ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo
|
ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo
|
||||||
.packages()
|
.packages()
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
use std::{
|
use std::{convert::TryFrom, env, ops, path::Path, process::Command};
|
||||||
env, ops,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
process::Command,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, format_err, Result};
|
||||||
use ra_arena::{Arena, Idx};
|
use ra_arena::{Arena, Idx};
|
||||||
|
|
||||||
use crate::output;
|
use crate::output;
|
||||||
|
use paths::{AbsPath, AbsPathBuf};
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct Sysroot {
|
pub struct Sysroot {
|
||||||
|
@ -21,7 +18,7 @@ pub type SysrootCrate = Idx<SysrootCrateData>;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SysrootCrateData {
|
pub struct SysrootCrateData {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub root: PathBuf,
|
pub root: AbsPathBuf,
|
||||||
pub deps: Vec<SysrootCrate>,
|
pub deps: Vec<SysrootCrate>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +50,7 @@ impl Sysroot {
|
||||||
self.crates.iter().map(|(id, _data)| id)
|
self.crates.iter().map(|(id, _data)| id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn discover(cargo_toml: &Path) -> Result<Sysroot> {
|
pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> {
|
||||||
let src = get_or_install_rust_src(cargo_toml)?;
|
let src = get_or_install_rust_src(cargo_toml)?;
|
||||||
let mut sysroot = Sysroot { crates: Arena::default() };
|
let mut sysroot = Sysroot { crates: Arena::default() };
|
||||||
for name in SYSROOT_CRATES.trim().lines() {
|
for name in SYSROOT_CRATES.trim().lines() {
|
||||||
|
@ -86,16 +83,18 @@ impl Sysroot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> {
|
fn get_or_install_rust_src(cargo_toml: &AbsPath) -> Result<AbsPathBuf> {
|
||||||
if let Ok(path) = env::var("RUST_SRC_PATH") {
|
if let Ok(path) = env::var("RUST_SRC_PATH") {
|
||||||
return Ok(path.into());
|
let path = AbsPathBuf::try_from(path.as_str())
|
||||||
|
.map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?;
|
||||||
|
return Ok(path);
|
||||||
}
|
}
|
||||||
let current_dir = cargo_toml.parent().unwrap();
|
let current_dir = cargo_toml.parent().unwrap();
|
||||||
let mut rustc = Command::new(ra_toolchain::rustc());
|
let mut rustc = Command::new(ra_toolchain::rustc());
|
||||||
rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
|
rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
|
||||||
let rustc_output = output(rustc)?;
|
let rustc_output = output(rustc)?;
|
||||||
let stdout = String::from_utf8(rustc_output.stdout)?;
|
let stdout = String::from_utf8(rustc_output.stdout)?;
|
||||||
let sysroot_path = Path::new(stdout.trim());
|
let sysroot_path = AbsPath::assert(Path::new(stdout.trim()));
|
||||||
let src_path = sysroot_path.join("lib/rustlib/src/rust/src");
|
let src_path = sysroot_path.join("lib/rustlib/src/rust/src");
|
||||||
|
|
||||||
if !src_path.exists() {
|
if !src_path.exists() {
|
||||||
|
@ -116,7 +115,7 @@ fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SysrootCrateData {
|
impl SysrootCrateData {
|
||||||
pub fn root_dir(&self) -> &Path {
|
pub fn root_dir(&self) -> &AbsPath {
|
||||||
self.root.parent().unwrap()
|
self.root.parent().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
|
//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
|
||||||
mod args;
|
mod args;
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use lsp_server::Connection;
|
use lsp_server::Connection;
|
||||||
use rust_analyzer::{
|
use rust_analyzer::{
|
||||||
cli,
|
cli,
|
||||||
|
@ -10,9 +12,11 @@ use rust_analyzer::{
|
||||||
from_json, Result,
|
from_json, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::args::HelpPrinted;
|
use ra_db::AbsPathBuf;
|
||||||
use ra_project_model::ProjectManifest;
|
use ra_project_model::ProjectManifest;
|
||||||
|
|
||||||
|
use crate::args::HelpPrinted;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
setup_logging()?;
|
setup_logging()?;
|
||||||
let args = match args::Args::parse()? {
|
let args = match args::Args::parse()? {
|
||||||
|
@ -20,6 +24,9 @@ fn main() -> Result<()> {
|
||||||
Err(HelpPrinted) => return Ok(()),
|
Err(HelpPrinted) => return Ok(()),
|
||||||
};
|
};
|
||||||
match args.command {
|
match args.command {
|
||||||
|
args::Command::RunServer => run_server()?,
|
||||||
|
args::Command::ProcMacro => ra_proc_macro_srv::cli::run()?,
|
||||||
|
|
||||||
args::Command::Parse { no_dump } => cli::parse(no_dump)?,
|
args::Command::Parse { no_dump } => cli::parse(no_dump)?,
|
||||||
args::Command::Symbols => cli::symbols()?,
|
args::Command::Symbols => cli::symbols()?,
|
||||||
args::Command::Highlight { rainbow } => cli::highlight(rainbow)?,
|
args::Command::Highlight { rainbow } => cli::highlight(rainbow)?,
|
||||||
|
@ -41,7 +48,6 @@ fn main() -> Result<()> {
|
||||||
load_output_dirs,
|
load_output_dirs,
|
||||||
with_proc_macro,
|
with_proc_macro,
|
||||||
)?,
|
)?,
|
||||||
|
|
||||||
args::Command::Bench { path, what, load_output_dirs, with_proc_macro } => {
|
args::Command::Bench { path, what, load_output_dirs, with_proc_macro } => {
|
||||||
cli::analysis_bench(
|
cli::analysis_bench(
|
||||||
args.verbosity,
|
args.verbosity,
|
||||||
|
@ -51,13 +57,9 @@ fn main() -> Result<()> {
|
||||||
with_proc_macro,
|
with_proc_macro,
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
|
|
||||||
args::Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } => {
|
args::Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } => {
|
||||||
cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)?
|
cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)?
|
||||||
}
|
}
|
||||||
|
|
||||||
args::Command::ProcMacro => run_proc_macro_srv()?,
|
|
||||||
args::Command::RunServer => run_server()?,
|
|
||||||
args::Command::Version => println!("rust-analyzer {}", env!("REV")),
|
args::Command::Version => println!("rust-analyzer {}", env!("REV")),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -70,11 +72,6 @@ fn setup_logging() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_proc_macro_srv() -> Result<()> {
|
|
||||||
ra_proc_macro_srv::cli::run()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_server() -> Result<()> {
|
fn run_server() -> Result<()> {
|
||||||
log::info!("lifecycle: server started");
|
log::info!("lifecycle: server started");
|
||||||
|
|
||||||
|
@ -103,14 +100,23 @@ fn run_server() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = {
|
let config = {
|
||||||
let mut config = Config::default();
|
let root_path = match initialize_params
|
||||||
|
.root_uri
|
||||||
|
.and_then(|it| it.to_file_path().ok())
|
||||||
|
.and_then(|it| AbsPathBuf::try_from(it).ok())
|
||||||
|
{
|
||||||
|
Some(it) => it,
|
||||||
|
None => {
|
||||||
|
let cwd = std::env::current_dir()?;
|
||||||
|
AbsPathBuf::assert(cwd)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut config = Config::new(root_path);
|
||||||
if let Some(value) = &initialize_params.initialization_options {
|
if let Some(value) = &initialize_params.initialization_options {
|
||||||
config.update(value);
|
config.update(value);
|
||||||
}
|
}
|
||||||
config.update_caps(&initialize_params.capabilities);
|
config.update_caps(&initialize_params.capabilities);
|
||||||
let cwd = std::env::current_dir()?;
|
|
||||||
config.root_path =
|
|
||||||
initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
|
|
||||||
|
|
||||||
if config.linked_projects.is_empty() {
|
if config.linked_projects.is_empty() {
|
||||||
let workspace_roots = initialize_params
|
let workspace_roots = initialize_params
|
||||||
|
@ -119,6 +125,7 @@ fn run_server() -> Result<()> {
|
||||||
workspaces
|
workspaces
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|it| it.uri.to_file_path().ok())
|
.filter_map(|it| it.uri.to_file_path().ok())
|
||||||
|
.filter_map(|it| AbsPathBuf::try_from(it).ok())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
.filter(|workspaces| !workspaces.is_empty())
|
.filter(|workspaces| !workspaces.is_empty())
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub fn load_cargo(
|
||||||
load_out_dirs_from_check: bool,
|
load_out_dirs_from_check: bool,
|
||||||
with_proc_macro: bool,
|
with_proc_macro: bool,
|
||||||
) -> Result<(AnalysisHost, vfs::Vfs)> {
|
) -> Result<(AnalysisHost, vfs::Vfs)> {
|
||||||
let root = std::env::current_dir()?.join(root);
|
let root = AbsPathBuf::assert(std::env::current_dir()?.join(root));
|
||||||
let root = ProjectManifest::discover_single(&root)?;
|
let root = ProjectManifest::discover_single(&root)?;
|
||||||
let ws = ProjectWorkspace::load(
|
let ws = ProjectWorkspace::load(
|
||||||
root,
|
root,
|
||||||
|
|
|
@ -11,6 +11,7 @@ use std::{ffi::OsString, path::PathBuf};
|
||||||
|
|
||||||
use crate::diagnostics::DiagnosticsConfig;
|
use crate::diagnostics::DiagnosticsConfig;
|
||||||
use lsp_types::ClientCapabilities;
|
use lsp_types::ClientCapabilities;
|
||||||
|
use ra_db::AbsPathBuf;
|
||||||
use ra_flycheck::FlycheckConfig;
|
use ra_flycheck::FlycheckConfig;
|
||||||
use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
|
use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
|
||||||
use ra_project_model::{CargoConfig, JsonProject, ProjectManifest};
|
use ra_project_model::{CargoConfig, JsonProject, ProjectManifest};
|
||||||
|
@ -40,7 +41,7 @@ pub struct Config {
|
||||||
|
|
||||||
pub with_sysroot: bool,
|
pub with_sysroot: bool,
|
||||||
pub linked_projects: Vec<LinkedProject>,
|
pub linked_projects: Vec<LinkedProject>,
|
||||||
pub root_path: PathBuf,
|
pub root_path: AbsPathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -131,8 +132,8 @@ pub struct ClientCapsConfig {
|
||||||
pub hover_actions: bool,
|
pub hover_actions: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Config {
|
||||||
fn default() -> Self {
|
pub fn new(root_path: AbsPathBuf) -> Self {
|
||||||
Config {
|
Config {
|
||||||
client_caps: ClientCapsConfig::default(),
|
client_caps: ClientCapsConfig::default(),
|
||||||
|
|
||||||
|
@ -171,18 +172,16 @@ impl Default for Config {
|
||||||
lens: LensConfig::default(),
|
lens: LensConfig::default(),
|
||||||
hover: HoverConfig::default(),
|
hover: HoverConfig::default(),
|
||||||
linked_projects: Vec::new(),
|
linked_projects: Vec::new(),
|
||||||
root_path: PathBuf::new(),
|
root_path,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub fn update(&mut self, value: &serde_json::Value) {
|
pub fn update(&mut self, value: &serde_json::Value) {
|
||||||
log::info!("Config::update({:#})", value);
|
log::info!("Config::update({:#})", value);
|
||||||
|
|
||||||
let client_caps = self.client_caps.clone();
|
let client_caps = self.client_caps.clone();
|
||||||
*self = Default::default();
|
*self = Config::new(self.root_path.clone());
|
||||||
self.client_caps = client_caps;
|
self.client_caps = client_caps;
|
||||||
|
|
||||||
set(value, "/withSysroot", &mut self.with_sysroot);
|
set(value, "/withSysroot", &mut self.with_sysroot);
|
||||||
|
@ -279,10 +278,13 @@ impl Config {
|
||||||
self.linked_projects.clear();
|
self.linked_projects.clear();
|
||||||
for linked_project in linked_projects {
|
for linked_project in linked_projects {
|
||||||
let linked_project = match linked_project {
|
let linked_project = match linked_project {
|
||||||
ManifestOrJsonProject::Manifest(it) => match ProjectManifest::from_manifest_file(it) {
|
ManifestOrJsonProject::Manifest(it) => {
|
||||||
|
let path = self.root_path.join(it);
|
||||||
|
match ProjectManifest::from_manifest_file(path) {
|
||||||
Ok(it) => it.into(),
|
Ok(it) => it.into(),
|
||||||
Err(_) => continue,
|
Err(_) => continue,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ManifestOrJsonProject::JsonProject(it) => it.into(),
|
ManifestOrJsonProject::JsonProject(it) => it.into(),
|
||||||
};
|
};
|
||||||
self.linked_projects.push(linked_project);
|
self.linked_projects.push(linked_project);
|
||||||
|
|
|
@ -310,11 +310,10 @@ impl ProjectFolders {
|
||||||
|
|
||||||
let mut file_set_roots: Vec<VfsPath> = vec![];
|
let mut file_set_roots: Vec<VfsPath> = vec![];
|
||||||
|
|
||||||
let path = AbsPathBuf::try_from(path).unwrap();
|
|
||||||
let entry = if root.is_member() {
|
let entry = if root.is_member() {
|
||||||
vfs::loader::Entry::local_cargo_package(path.clone())
|
vfs::loader::Entry::local_cargo_package(path.to_path_buf())
|
||||||
} else {
|
} else {
|
||||||
vfs::loader::Entry::cargo_package_dependency(path.clone())
|
vfs::loader::Entry::cargo_package_dependency(path.to_path_buf())
|
||||||
};
|
};
|
||||||
res.load.push(entry);
|
res.load.push(entry);
|
||||||
if root.is_member() {
|
if root.is_member() {
|
||||||
|
@ -329,7 +328,7 @@ impl ProjectFolders {
|
||||||
}
|
}
|
||||||
file_set_roots.push(out_dir.into());
|
file_set_roots.push(out_dir.into());
|
||||||
}
|
}
|
||||||
file_set_roots.push(path.into());
|
file_set_roots.push(path.to_path_buf().into());
|
||||||
|
|
||||||
if root.is_member() {
|
if root.is_member() {
|
||||||
local_filesets.push(fsc.len());
|
local_filesets.push(fsc.len());
|
||||||
|
|
|
@ -17,6 +17,7 @@ use serde_json::{to_string_pretty, Value};
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
use test_utils::{find_mismatch, Fixture};
|
use test_utils::{find_mismatch, Fixture};
|
||||||
|
|
||||||
|
use ra_db::AbsPathBuf;
|
||||||
use ra_project_model::ProjectManifest;
|
use ra_project_model::ProjectManifest;
|
||||||
use rust_analyzer::{
|
use rust_analyzer::{
|
||||||
config::{ClientCapsConfig, Config, FilesConfig, FilesWatcher, LinkedProject},
|
config::{ClientCapsConfig, Config, FilesConfig, FilesWatcher, LinkedProject},
|
||||||
|
@ -70,10 +71,11 @@ impl<'a> Project<'a> {
|
||||||
fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
|
fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tmp_dir_path = AbsPathBuf::assert(tmp_dir.path().to_path_buf());
|
||||||
let mut roots =
|
let mut roots =
|
||||||
self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect::<Vec<_>>();
|
self.roots.into_iter().map(|root| tmp_dir_path.join(root)).collect::<Vec<_>>();
|
||||||
if roots.is_empty() {
|
if roots.is_empty() {
|
||||||
roots.push(tmp_dir.path().to_path_buf());
|
roots.push(tmp_dir_path.clone());
|
||||||
}
|
}
|
||||||
let linked_projects = roots
|
let linked_projects = roots
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -91,7 +93,7 @@ impl<'a> Project<'a> {
|
||||||
with_sysroot: self.with_sysroot,
|
with_sysroot: self.with_sysroot,
|
||||||
linked_projects,
|
linked_projects,
|
||||||
files: FilesConfig { watcher: FilesWatcher::Client, exclude: Vec::new() },
|
files: FilesConfig { watcher: FilesWatcher::Client, exclude: Vec::new() },
|
||||||
..Config::default()
|
..Config::new(tmp_dir_path)
|
||||||
};
|
};
|
||||||
if let Some(f) = &self.config {
|
if let Some(f) = &self.config {
|
||||||
f(&mut config)
|
f(&mut config)
|
||||||
|
|
Loading…
Reference in a new issue