5029: Be more explicit about absolute paths at various places r=matklad a=matklad



bors r+
🤖

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2020-06-24 12:52:05 +00:00 committed by GitHub
commit 44721dca66
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 126 additions and 94 deletions

1
Cargo.lock generated
View file

@ -1174,6 +1174,7 @@ dependencies = [
"anyhow",
"cargo_metadata",
"log",
"paths",
"ra_arena",
"ra_cfg",
"ra_db",

View file

@ -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 {
type Error = 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 {
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 {
AbsPath::new_unchecked(self.0.as_path())
AbsPath::assert(self.0.as_path())
}
pub fn pop(&mut self) -> bool {
self.0.pop()
@ -77,15 +93,19 @@ impl<'a> TryFrom<&'a Path> for &'a AbsPath {
if !path.is_absolute() {
return Err(path);
}
Ok(AbsPath::new_unchecked(path))
Ok(AbsPath::assert(path))
}
}
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) }
}
pub fn parent(&self) -> Option<&AbsPath> {
self.0.parent().map(AbsPath::assert)
}
pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
self.as_ref().join(path).try_into().unwrap()
}

View file

@ -18,6 +18,7 @@ ra_cfg = { path = "../ra_cfg" }
ra_db = { path = "../ra_db" }
ra_toolchain = { path = "../ra_toolchain" }
ra_proc_macro = { path = "../ra_proc_macro" }
paths = { path = "../paths" }
serde = { version = "1.0.106", features = ["derive"] }
serde_json = "1.0.48"

View file

@ -1,14 +1,10 @@
//! FIXME: write short doc here
use std::{
ffi::OsStr,
ops,
path::{Path, PathBuf},
process::Command,
};
use std::{ffi::OsStr, ops, path::Path, process::Command};
use anyhow::{Context, Result};
use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
use paths::{AbsPath, AbsPathBuf};
use ra_arena::{Arena, Idx};
use ra_db::Edition;
use rustc_hash::FxHashMap;
@ -20,11 +16,14 @@ use rustc_hash::FxHashMap;
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
/// while this knows about `Packages` & `Targets`: purely cargo-related
/// concepts.
///
/// We use absolute paths here, `cargo metadata` guarantees to always produce
/// abs paths.
#[derive(Debug, Clone)]
pub struct CargoWorkspace {
packages: Arena<PackageData>,
targets: Arena<TargetData>,
workspace_root: PathBuf,
workspace_root: AbsPathBuf,
}
impl ops::Index<Package> for CargoWorkspace {
@ -80,15 +79,15 @@ pub type Target = Idx<TargetData>;
pub struct PackageData {
pub version: String,
pub name: String,
pub manifest: PathBuf,
pub manifest: AbsPathBuf,
pub targets: Vec<Target>,
pub is_member: bool,
pub dependencies: Vec<PackageDependency>,
pub edition: Edition,
pub features: Vec<String>,
pub cfgs: Vec<String>,
pub out_dir: Option<PathBuf>,
pub proc_macro_dylib_path: Option<PathBuf>,
pub out_dir: Option<AbsPathBuf>,
pub proc_macro_dylib_path: Option<AbsPathBuf>,
}
#[derive(Debug, Clone)]
@ -101,7 +100,7 @@ pub struct PackageDependency {
pub struct TargetData {
pub package: Package,
pub name: String,
pub root: PathBuf,
pub root: AbsPathBuf,
pub kind: TargetKind,
pub is_proc_macro: bool,
}
@ -135,7 +134,7 @@ impl TargetKind {
}
impl PackageData {
pub fn root(&self) -> &Path {
pub fn root(&self) -> &AbsPath {
self.manifest.parent().unwrap()
}
}
@ -193,7 +192,7 @@ impl CargoWorkspace {
let pkg = packages.alloc(PackageData {
name,
version: version.to_string(),
manifest: manifest_path,
manifest: AbsPathBuf::assert(manifest_path),
targets: Vec::new(),
is_member,
edition,
@ -210,7 +209,7 @@ impl CargoWorkspace {
let tgt = targets.alloc(TargetData {
package: pkg,
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()),
is_proc_macro,
});
@ -246,16 +245,17 @@ impl CargoWorkspace {
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 {
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()
.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()
.copied()
}
@ -279,8 +279,8 @@ impl CargoWorkspace {
#[derive(Debug, Clone, Default)]
pub struct ExternResources {
out_dirs: FxHashMap<PackageId, PathBuf>,
proc_dylib_paths: FxHashMap<PackageId, PathBuf>,
out_dirs: FxHashMap<PackageId, AbsPathBuf>,
proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>,
cfgs: FxHashMap<PackageId, Vec<String>>,
}
@ -308,6 +308,7 @@ pub fn load_extern_resources(
if let Ok(message) = message {
match message {
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.cfgs.insert(package_id, cfgs);
}
@ -317,7 +318,8 @@ pub fn load_extern_resources(
// Skip rmeta file
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);
}
}
}

View file

@ -7,11 +7,12 @@ mod sysroot;
use std::{
fs::{read_dir, File, ReadDir},
io::{self, BufReader},
path::{Path, PathBuf},
path::Path,
process::{Command, Output},
};
use anyhow::{bail, Context, Result};
use paths::{AbsPath, AbsPathBuf};
use ra_cfg::CfgOptions;
use ra_db::{CrateGraph, CrateName, Edition, Env, FileId};
use rustc_hash::{FxHashMap, FxHashSet};
@ -29,7 +30,7 @@ pub enum ProjectWorkspace {
/// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
Cargo { cargo: CargoWorkspace, sysroot: Sysroot },
/// 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.
@ -38,22 +39,22 @@ pub enum ProjectWorkspace {
#[derive(Debug, Clone)]
pub struct PackageRoot {
/// Path to the root folder
path: PathBuf,
path: AbsPathBuf,
/// Is a member of the current workspace
is_member: bool,
out_dir: Option<PathBuf>,
out_dir: Option<AbsPathBuf>,
}
impl PackageRoot {
pub fn new_member(path: PathBuf) -> PackageRoot {
pub fn new_member(path: AbsPathBuf) -> PackageRoot {
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 }
}
pub fn path(&self) -> &Path {
pub fn path(&self) -> &AbsPath {
&self.path
}
pub fn out_dir(&self) -> Option<&Path> {
pub fn out_dir(&self) -> Option<&AbsPath> {
self.out_dir.as_deref()
}
pub fn is_member(&self) -> bool {
@ -63,12 +64,12 @@ impl PackageRoot {
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum ProjectManifest {
ProjectJson(PathBuf),
CargoToml(PathBuf),
ProjectJson(AbsPathBuf),
CargoToml(AbsPathBuf),
}
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") {
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())
}
pub fn discover_single(path: &Path) -> Result<ProjectManifest> {
pub fn discover_single(path: &AbsPath) -> Result<ProjectManifest> {
let mut candidates = ProjectManifest::discover(path)?;
let res = match candidates.pop() {
None => bail!("no projects"),
@ -91,23 +92,23 @@ impl ProjectManifest {
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") {
return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
}
return find_cargo_toml(path)
.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") {
Some(it) => Ok(vec![it]),
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) {
return Some(path.to_owned());
return Some(path.to_path_buf());
}
let mut curr = Some(path);
@ -123,17 +124,18 @@ impl ProjectManifest {
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
entities
.filter_map(Result::ok)
.map(|it| it.path().join("Cargo.toml"))
.filter(|it| it.exists())
.map(AbsPathBuf::assert)
.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
.iter()
.filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
@ -158,15 +160,12 @@ impl ProjectWorkspace {
format!("Failed to open json file {}", project_json.display())
})?;
let reader = BufReader::new(file);
let project_location = match project_json.parent() {
Some(parent) => PathBuf::from(parent),
None => PathBuf::new(),
};
let project_location = project_json.parent().unwrap().to_path_buf();
ProjectWorkspace::Json {
project: from_reader(reader).with_context(|| {
format!("Failed to deserialize json file {}", project_json.display())
})?,
project_location: project_location,
project_location,
}
}
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 {
ProjectWorkspace::Json { project, .. } => project
ProjectWorkspace::Json { project, project_location } => project
.crates
.iter()
.filter_map(|krate| krate.proc_macro_dylib_path.as_ref())
.cloned()
.map(|it| project_location.join(it))
.collect(),
ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo
.packages()

View file

@ -1,15 +1,12 @@
//! FIXME: write short doc here
use std::{
env, ops,
path::{Path, PathBuf},
process::Command,
};
use std::{convert::TryFrom, env, ops, path::Path, process::Command};
use anyhow::{bail, Result};
use anyhow::{bail, format_err, Result};
use ra_arena::{Arena, Idx};
use crate::output;
use paths::{AbsPath, AbsPathBuf};
#[derive(Default, Debug, Clone)]
pub struct Sysroot {
@ -21,7 +18,7 @@ pub type SysrootCrate = Idx<SysrootCrateData>;
#[derive(Debug, Clone)]
pub struct SysrootCrateData {
pub name: String,
pub root: PathBuf,
pub root: AbsPathBuf,
pub deps: Vec<SysrootCrate>,
}
@ -53,7 +50,7 @@ impl Sysroot {
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 mut sysroot = Sysroot { crates: Arena::default() };
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") {
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 mut rustc = Command::new(ra_toolchain::rustc());
rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
let rustc_output = output(rustc)?;
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");
if !src_path.exists() {
@ -116,7 +115,7 @@ fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> {
}
impl SysrootCrateData {
pub fn root_dir(&self) -> &Path {
pub fn root_dir(&self) -> &AbsPath {
self.root.parent().unwrap()
}
}

View file

@ -3,6 +3,8 @@
//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
mod args;
use std::convert::TryFrom;
use lsp_server::Connection;
use rust_analyzer::{
cli,
@ -10,9 +12,11 @@ use rust_analyzer::{
from_json, Result,
};
use crate::args::HelpPrinted;
use ra_db::AbsPathBuf;
use ra_project_model::ProjectManifest;
use crate::args::HelpPrinted;
fn main() -> Result<()> {
setup_logging()?;
let args = match args::Args::parse()? {
@ -20,6 +24,9 @@ fn main() -> Result<()> {
Err(HelpPrinted) => return Ok(()),
};
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::Symbols => cli::symbols()?,
args::Command::Highlight { rainbow } => cli::highlight(rainbow)?,
@ -41,7 +48,6 @@ fn main() -> Result<()> {
load_output_dirs,
with_proc_macro,
)?,
args::Command::Bench { path, what, load_output_dirs, with_proc_macro } => {
cli::analysis_bench(
args.verbosity,
@ -51,13 +57,9 @@ fn main() -> Result<()> {
with_proc_macro,
)?
}
args::Command::Diagnostics { path, 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")),
}
Ok(())
@ -70,11 +72,6 @@ fn setup_logging() -> Result<()> {
Ok(())
}
fn run_proc_macro_srv() -> Result<()> {
ra_proc_macro_srv::cli::run()?;
Ok(())
}
fn run_server() -> Result<()> {
log::info!("lifecycle: server started");
@ -103,14 +100,23 @@ fn run_server() -> Result<()> {
}
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 {
config.update(value);
}
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() {
let workspace_roots = initialize_params
@ -119,6 +125,7 @@ fn run_server() -> Result<()> {
workspaces
.into_iter()
.filter_map(|it| it.uri.to_file_path().ok())
.filter_map(|it| AbsPathBuf::try_from(it).ok())
.collect::<Vec<_>>()
})
.filter(|workspaces| !workspaces.is_empty())

View file

@ -16,7 +16,7 @@ pub fn load_cargo(
load_out_dirs_from_check: bool,
with_proc_macro: bool,
) -> 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 ws = ProjectWorkspace::load(
root,

View file

@ -11,6 +11,7 @@ use std::{ffi::OsString, path::PathBuf};
use crate::diagnostics::DiagnosticsConfig;
use lsp_types::ClientCapabilities;
use ra_db::AbsPathBuf;
use ra_flycheck::FlycheckConfig;
use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
use ra_project_model::{CargoConfig, JsonProject, ProjectManifest};
@ -40,7 +41,7 @@ pub struct Config {
pub with_sysroot: bool,
pub linked_projects: Vec<LinkedProject>,
pub root_path: PathBuf,
pub root_path: AbsPathBuf,
}
#[derive(Debug, Clone)]
@ -131,8 +132,8 @@ pub struct ClientCapsConfig {
pub hover_actions: bool,
}
impl Default for Config {
fn default() -> Self {
impl Config {
pub fn new(root_path: AbsPathBuf) -> Self {
Config {
client_caps: ClientCapsConfig::default(),
@ -171,18 +172,16 @@ impl Default for Config {
lens: LensConfig::default(),
hover: HoverConfig::default(),
linked_projects: Vec::new(),
root_path: PathBuf::new(),
root_path,
}
}
}
impl Config {
#[rustfmt::skip]
pub fn update(&mut self, value: &serde_json::Value) {
log::info!("Config::update({:#})", value);
let client_caps = self.client_caps.clone();
*self = Default::default();
*self = Config::new(self.root_path.clone());
self.client_caps = client_caps;
set(value, "/withSysroot", &mut self.with_sysroot);
@ -279,9 +278,12 @@ impl Config {
self.linked_projects.clear();
for linked_project in linked_projects {
let linked_project = match linked_project {
ManifestOrJsonProject::Manifest(it) => match ProjectManifest::from_manifest_file(it) {
Ok(it) => it.into(),
Err(_) => continue,
ManifestOrJsonProject::Manifest(it) => {
let path = self.root_path.join(it);
match ProjectManifest::from_manifest_file(path) {
Ok(it) => it.into(),
Err(_) => continue,
}
}
ManifestOrJsonProject::JsonProject(it) => it.into(),
};

View file

@ -310,11 +310,10 @@ impl ProjectFolders {
let mut file_set_roots: Vec<VfsPath> = vec![];
let path = AbsPathBuf::try_from(path).unwrap();
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 {
vfs::loader::Entry::cargo_package_dependency(path.clone())
vfs::loader::Entry::cargo_package_dependency(path.to_path_buf())
};
res.load.push(entry);
if root.is_member() {
@ -329,7 +328,7 @@ impl ProjectFolders {
}
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() {
local_filesets.push(fsc.len());

View file

@ -17,6 +17,7 @@ use serde_json::{to_string_pretty, Value};
use tempfile::TempDir;
use test_utils::{find_mismatch, Fixture};
use ra_db::AbsPathBuf;
use ra_project_model::ProjectManifest;
use rust_analyzer::{
config::{ClientCapsConfig, Config, FilesConfig, FilesWatcher, LinkedProject},
@ -70,10 +71,11 @@ impl<'a> Project<'a> {
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 =
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() {
roots.push(tmp_dir.path().to_path_buf());
roots.push(tmp_dir_path.clone());
}
let linked_projects = roots
.into_iter()
@ -91,7 +93,7 @@ impl<'a> Project<'a> {
with_sysroot: self.with_sysroot,
linked_projects,
files: FilesConfig { watcher: FilesWatcher::Client, exclude: Vec::new() },
..Config::default()
..Config::new(tmp_dir_path)
};
if let Some(f) = &self.config {
f(&mut config)