Auto merge of #15161 - lowr:patch/fixture-metadata-library, r=Veykril
internal: add `library` fixture meta
Currently, there is no way to specify `CrateOrigin` of a file fixture ([this] might be a bug?). This PR adds `library` meta to explicitly specify the fixture to be `CrateOrigin::Library` and also makes sure crates that belong to a library source root are set `CrateOrigin::Library`.
(`library` isn't really the best name. It essentially means that the crate is outside workspace but `non_workspace_member` feels a bit too long. Suggestions for the better name would be appreciated)
Additionally:
- documents the fixture meta syntax as thoroughly as possible
- refactors relevant code
[this]: 4b06d3c595/crates/base-db/src/fixture.rs (L450)
This commit is contained in:
commit
ae89ca3fbb
@ -102,6 +102,8 @@ pub struct ChangeFixture {
|
||||
pub change: Change,
|
||||
}
|
||||
|
||||
const SOURCE_ROOT_PREFIX: &str = "/";
|
||||
|
||||
impl ChangeFixture {
|
||||
pub fn parse(ra_fixture: &str) -> ChangeFixture {
|
||||
Self::parse_with_proc_macros(ra_fixture, Vec::new())
|
||||
@ -131,7 +133,6 @@ pub fn parse_with_proc_macros(
|
||||
|
||||
let mut file_set = FileSet::default();
|
||||
let mut current_source_root_kind = SourceRootKind::Local;
|
||||
let source_root_prefix = "/".to_string();
|
||||
let mut file_id = FileId(0);
|
||||
let mut roots = Vec::new();
|
||||
|
||||
@ -151,19 +152,23 @@ pub fn parse_with_proc_macros(
|
||||
entry.text.clone()
|
||||
};
|
||||
|
||||
let meta = FileMeta::from(entry);
|
||||
assert!(meta.path.starts_with(&source_root_prefix));
|
||||
let meta = FileMeta::from_fixture(entry, current_source_root_kind);
|
||||
assert!(meta.path.starts_with(SOURCE_ROOT_PREFIX));
|
||||
if !meta.deps.is_empty() {
|
||||
assert!(meta.krate.is_some(), "can't specify deps without naming the crate")
|
||||
}
|
||||
|
||||
if let Some(kind) = &meta.introduce_new_source_root {
|
||||
let root = match current_source_root_kind {
|
||||
if let Some(kind) = meta.introduce_new_source_root {
|
||||
assert!(
|
||||
meta.krate.is_some(),
|
||||
"new_source_root meta doesn't make sense without crate meta"
|
||||
);
|
||||
let prev_kind = mem::replace(&mut current_source_root_kind, kind);
|
||||
let prev_root = match prev_kind {
|
||||
SourceRootKind::Local => SourceRoot::new_local(mem::take(&mut file_set)),
|
||||
SourceRootKind::Library => SourceRoot::new_library(mem::take(&mut file_set)),
|
||||
};
|
||||
roots.push(root);
|
||||
current_source_root_kind = *kind;
|
||||
roots.push(prev_root);
|
||||
}
|
||||
|
||||
if let Some((krate, origin, version)) = meta.krate {
|
||||
@ -185,7 +190,7 @@ pub fn parse_with_proc_macros(
|
||||
Some(toolchain),
|
||||
);
|
||||
let prev = crates.insert(crate_name.clone(), crate_id);
|
||||
assert!(prev.is_none());
|
||||
assert!(prev.is_none(), "multiple crates with same name: {}", crate_name);
|
||||
for dep in meta.deps {
|
||||
let prelude = meta.extern_prelude.contains(&dep);
|
||||
let dep = CrateName::normalize_dashes(&dep);
|
||||
@ -442,51 +447,74 @@ struct FileMeta {
|
||||
target_data_layout: Option<String>,
|
||||
}
|
||||
|
||||
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::Local { repo: Some(url.to_owned()), name: None })
|
||||
}
|
||||
_ => panic!("Bad crates.io parameter: {data}"),
|
||||
},
|
||||
_ => panic!("Bad string for crate origin: {b}"),
|
||||
};
|
||||
(a.to_owned(), origin, Some(version.to_string()))
|
||||
} else {
|
||||
let crate_origin = match LangCrateOrigin::from(&*crate_str) {
|
||||
LangCrateOrigin::Other => CrateOrigin::Local { repo: None, name: None },
|
||||
origin => CrateOrigin::Lang(origin),
|
||||
};
|
||||
(crate_str, crate_origin, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fixture> for FileMeta {
|
||||
fn from(f: Fixture) -> FileMeta {
|
||||
impl FileMeta {
|
||||
fn from_fixture(f: Fixture, current_source_root_kind: SourceRootKind) -> Self {
|
||||
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()));
|
||||
for (k, v) in f.cfgs {
|
||||
if let Some(v) = v {
|
||||
cfg.insert_key_value(k.into(), v.into());
|
||||
} else {
|
||||
cfg.insert_atom(k.into());
|
||||
}
|
||||
}
|
||||
|
||||
let introduce_new_source_root = f.introduce_new_source_root.map(|kind| match &*kind {
|
||||
"local" => SourceRootKind::Local,
|
||||
"library" => SourceRootKind::Library,
|
||||
invalid => panic!("invalid source root kind '{invalid}'"),
|
||||
});
|
||||
let current_source_root_kind =
|
||||
introduce_new_source_root.unwrap_or(current_source_root_kind);
|
||||
|
||||
let deps = f.deps;
|
||||
FileMeta {
|
||||
Self {
|
||||
path: f.path,
|
||||
krate: f.krate.map(parse_crate),
|
||||
krate: f.krate.map(|it| parse_crate(it, current_source_root_kind, f.library)),
|
||||
extern_prelude: f.extern_prelude.unwrap_or_else(|| deps.clone()),
|
||||
deps,
|
||||
cfg,
|
||||
edition: f.edition.as_ref().map_or(Edition::CURRENT, |v| Edition::from_str(v).unwrap()),
|
||||
edition: f.edition.map_or(Edition::CURRENT, |v| Edition::from_str(&v).unwrap()),
|
||||
env: f.env.into_iter().collect(),
|
||||
introduce_new_source_root: f.introduce_new_source_root.map(|kind| match &*kind {
|
||||
"local" => SourceRootKind::Local,
|
||||
"library" => SourceRootKind::Library,
|
||||
invalid => panic!("invalid source root kind '{invalid}'"),
|
||||
}),
|
||||
introduce_new_source_root,
|
||||
target_data_layout: f.target_data_layout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_crate(
|
||||
crate_str: String,
|
||||
current_source_root_kind: SourceRootKind,
|
||||
explicit_non_workspace_member: bool,
|
||||
) -> (String, CrateOrigin, Option<String>) {
|
||||
// syntax:
|
||||
// "my_awesome_crate"
|
||||
// "my_awesome_crate@0.0.1,http://example.com"
|
||||
let (name, repo, version) = if let Some((name, remain)) = crate_str.split_once('@') {
|
||||
let (version, repo) =
|
||||
remain.split_once(',').expect("crate meta: found '@' without version and url");
|
||||
(name.to_owned(), Some(repo.to_owned()), Some(version.to_owned()))
|
||||
} else {
|
||||
(crate_str, None, None)
|
||||
};
|
||||
|
||||
let non_workspace_member = explicit_non_workspace_member
|
||||
|| matches!(current_source_root_kind, SourceRootKind::Library);
|
||||
|
||||
let origin = match LangCrateOrigin::from(&*name) {
|
||||
LangCrateOrigin::Other => {
|
||||
let name = name.clone();
|
||||
if non_workspace_member {
|
||||
CrateOrigin::Library { repo, name }
|
||||
} else {
|
||||
CrateOrigin::Local { repo, name: Some(name) }
|
||||
}
|
||||
}
|
||||
origin => CrateOrigin::Lang(origin),
|
||||
};
|
||||
|
||||
(name, origin, version)
|
||||
}
|
||||
|
||||
// Identity mapping
|
||||
#[derive(Debug)]
|
||||
struct IdentityProcMacroExpander;
|
||||
|
@ -138,12 +138,12 @@ fn deref(&self) -> &str {
|
||||
}
|
||||
}
|
||||
|
||||
/// Origin of the crates. It is used in emitting monikers.
|
||||
/// Origin of the crates.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum CrateOrigin {
|
||||
/// Crates that are from the rustc workspace
|
||||
/// Crates that are from the rustc workspace.
|
||||
Rustc { name: String },
|
||||
/// Crates that are workspace members,
|
||||
/// Crates that are workspace members.
|
||||
Local { repo: Option<String>, name: Option<String> },
|
||||
/// Crates that are non member libraries.
|
||||
Library { repo: Option<String>, name: String },
|
||||
|
@ -320,7 +320,7 @@ fn basic() {
|
||||
fn main() {
|
||||
func$0();
|
||||
}
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub mod module {
|
||||
pub fn func() {}
|
||||
}
|
||||
@ -336,7 +336,7 @@ pub fn func() {}
|
||||
fn main() {
|
||||
func();
|
||||
}
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub mod module {
|
||||
pub fn func$0() {}
|
||||
}
|
||||
@ -351,7 +351,7 @@ pub fn func$0() {}
|
||||
fn moniker_for_trait() {
|
||||
check_moniker(
|
||||
r#"
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub mod module {
|
||||
pub trait MyTrait {
|
||||
pub fn func$0() {}
|
||||
@ -368,7 +368,7 @@ pub fn func$0() {}
|
||||
fn moniker_for_trait_constant() {
|
||||
check_moniker(
|
||||
r#"
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub mod module {
|
||||
pub trait MyTrait {
|
||||
const MY_CONST$0: u8;
|
||||
@ -385,7 +385,7 @@ pub trait MyTrait {
|
||||
fn moniker_for_trait_type() {
|
||||
check_moniker(
|
||||
r#"
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub mod module {
|
||||
pub trait MyTrait {
|
||||
type MyType$0;
|
||||
@ -402,7 +402,7 @@ pub trait MyTrait {
|
||||
fn moniker_for_trait_impl_function() {
|
||||
check_moniker(
|
||||
r#"
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub mod module {
|
||||
pub trait MyTrait {
|
||||
pub fn func() {}
|
||||
@ -430,7 +430,7 @@ fn moniker_for_field() {
|
||||
fn main() {
|
||||
let x = St { a$0: 2 };
|
||||
}
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub struct St {
|
||||
pub a: i32,
|
||||
}
|
||||
@ -450,7 +450,7 @@ fn no_moniker_for_local() {
|
||||
fn main() {
|
||||
func();
|
||||
}
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub mod module {
|
||||
pub fn func() {
|
||||
let x$0 = 2;
|
||||
|
@ -324,7 +324,7 @@ fn basic() {
|
||||
fn main() {
|
||||
func$0();
|
||||
}
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub mod example_mod {
|
||||
pub fn func() {}
|
||||
}
|
||||
@ -337,7 +337,7 @@ pub fn func() {}
|
||||
fn symbol_for_trait() {
|
||||
check_symbol(
|
||||
r#"
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub mod module {
|
||||
pub trait MyTrait {
|
||||
pub fn func$0() {}
|
||||
@ -352,7 +352,7 @@ pub fn func$0() {}
|
||||
fn symbol_for_trait_constant() {
|
||||
check_symbol(
|
||||
r#"
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub mod module {
|
||||
pub trait MyTrait {
|
||||
const MY_CONST$0: u8;
|
||||
@ -367,7 +367,7 @@ pub trait MyTrait {
|
||||
fn symbol_for_trait_type() {
|
||||
check_symbol(
|
||||
r#"
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub mod module {
|
||||
pub trait MyTrait {
|
||||
type MyType$0;
|
||||
@ -383,7 +383,7 @@ pub trait MyTrait {
|
||||
fn symbol_for_trait_impl_function() {
|
||||
check_symbol(
|
||||
r#"
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub mod module {
|
||||
pub trait MyTrait {
|
||||
pub fn func() {}
|
||||
@ -410,7 +410,7 @@ fn symbol_for_field() {
|
||||
fn main() {
|
||||
let x = St { a$0: 2 };
|
||||
}
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub struct St {
|
||||
pub a: i32,
|
||||
}
|
||||
@ -428,7 +428,7 @@ fn local_symbol_for_local() {
|
||||
fn main() {
|
||||
func();
|
||||
}
|
||||
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
|
||||
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
|
||||
pub mod module {
|
||||
pub fn func() {
|
||||
let x$0 = 2;
|
||||
|
@ -47,13 +47,10 @@
|
||||
//! ```
|
||||
//!
|
||||
//! Metadata allows specifying all settings and variables
|
||||
//! that are available in a real rust project:
|
||||
//! - crate names via `crate:cratename`
|
||||
//! - dependencies via `deps:dep1,dep2`
|
||||
//! - configuration settings via `cfg:dbg=false,opt_level=2`
|
||||
//! - environment variables via `env:PATH=/bin,RUST_LOG=debug`
|
||||
//! that are available in a real rust project. See [`Fixture`]
|
||||
//! for the syntax.
|
||||
//!
|
||||
//! Example using all available metadata:
|
||||
//! Example using some available metadata:
|
||||
//! ```
|
||||
//! "
|
||||
//! //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
|
||||
@ -68,17 +65,74 @@
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Fixture {
|
||||
/// Specifies the path for this file. It must start with "/".
|
||||
pub path: String,
|
||||
pub text: String,
|
||||
/// Defines a new crate and make this file its root module.
|
||||
///
|
||||
/// Version and repository URL of the crate can optionally be specified; if
|
||||
/// either one is specified, the other must also be specified.
|
||||
///
|
||||
/// Syntax:
|
||||
/// - `crate:my_awesome_lib`
|
||||
/// - `crate:my_awesome_lib@0.0.1,https://example.com/repo.git`
|
||||
pub krate: Option<String>,
|
||||
/// Specifies dependencies of this crate. This must be used with `crate` meta.
|
||||
///
|
||||
/// Syntax: `deps:hir-def,ide-assists`
|
||||
pub deps: Vec<String>,
|
||||
/// Limits crates in the extern prelude. The set of crate names must be a
|
||||
/// subset of `deps`. This must be used with `crate` meta.
|
||||
///
|
||||
/// If this is not specified, all the dependencies will be in the extern prelude.
|
||||
///
|
||||
/// Syntax: `extern-prelude:hir-def,ide-assists`
|
||||
pub extern_prelude: Option<Vec<String>>,
|
||||
pub cfg_atoms: Vec<String>,
|
||||
pub cfg_key_values: Vec<(String, String)>,
|
||||
/// Specifies configuration options to be enabled. Options may have associated
|
||||
/// values.
|
||||
///
|
||||
/// Syntax: `cfg:test,dbg=false,opt_level=2`
|
||||
pub cfgs: Vec<(String, Option<String>)>,
|
||||
/// Specifies the edition of this crate. This must be used with `crate` meta. If
|
||||
/// this is not specified, ([`base_db::input::Edition::CURRENT`]) will be used.
|
||||
/// This must be used with `crate` meta.
|
||||
///
|
||||
/// Syntax: `edition:2021`
|
||||
pub edition: Option<String>,
|
||||
/// Specifies environment variables.
|
||||
///
|
||||
/// Syntax: `env:PATH=/bin,RUST_LOG=debug`
|
||||
pub env: FxHashMap<String, String>,
|
||||
/// Introduces a new [source root](base_db::input::SourceRoot). This file **and
|
||||
/// the following files** will belong the new source root. This must be used
|
||||
/// with `crate` meta.
|
||||
///
|
||||
/// Use this if you want to test something that uses `SourceRoot::is_library()`
|
||||
/// to check editability.
|
||||
///
|
||||
/// Note that files before the first fixture with `new_source_root` meta will
|
||||
/// belong to an implicitly defined local source root.
|
||||
///
|
||||
/// Syntax:
|
||||
/// - `new_source_root:library`
|
||||
/// - `new_source_root:local`
|
||||
pub introduce_new_source_root: Option<String>,
|
||||
/// Explicitly declares this crate as a library outside current workspace. This
|
||||
/// must be used with `crate` meta.
|
||||
///
|
||||
/// This is implied if this file belongs to a library source root.
|
||||
///
|
||||
/// Use this if you want to test something that checks if a crate is a workspace
|
||||
/// member via [`CrateOrigin`](base_db::input::CrateOrigin).
|
||||
///
|
||||
/// Syntax: `library`
|
||||
pub library: bool,
|
||||
/// Specifies LLVM data layout to be used.
|
||||
///
|
||||
/// You probably don't want to manually specify this. See LLVM manual for the
|
||||
/// syntax, if you must: https://llvm.org/docs/LangRef.html#data-layout
|
||||
pub target_data_layout: Option<String>,
|
||||
/// Actual file contents. All meta comments are stripped.
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
pub struct MiniCore {
|
||||
@ -178,23 +232,28 @@ pub fn parse(ra_fixture: &str) -> Self {
|
||||
fn parse_meta_line(meta: &str) -> Fixture {
|
||||
assert!(meta.starts_with("//-"));
|
||||
let meta = meta["//-".len()..].trim();
|
||||
let components = meta.split_ascii_whitespace().collect::<Vec<_>>();
|
||||
let mut components = meta.split_ascii_whitespace();
|
||||
|
||||
let path = components[0].to_string();
|
||||
let path = components.next().expect("fixture meta must start with a path").to_string();
|
||||
assert!(path.starts_with('/'), "fixture path does not start with `/`: {path:?}");
|
||||
|
||||
let mut krate = None;
|
||||
let mut deps = Vec::new();
|
||||
let mut extern_prelude = None;
|
||||
let mut edition = None;
|
||||
let mut cfg_atoms = Vec::new();
|
||||
let mut cfg_key_values = Vec::new();
|
||||
let mut cfgs = Vec::new();
|
||||
let mut env = FxHashMap::default();
|
||||
let mut introduce_new_source_root = None;
|
||||
let mut library = false;
|
||||
let mut target_data_layout = Some(
|
||||
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128".to_string(),
|
||||
);
|
||||
for component in components[1..].iter() {
|
||||
for component in components {
|
||||
if component == "library" {
|
||||
library = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
let (key, value) =
|
||||
component.split_once(':').unwrap_or_else(|| panic!("invalid meta line: {meta:?}"));
|
||||
match key {
|
||||
@ -212,8 +271,8 @@ fn parse_meta_line(meta: &str) -> Fixture {
|
||||
"cfg" => {
|
||||
for entry in value.split(',') {
|
||||
match entry.split_once('=') {
|
||||
Some((k, v)) => cfg_key_values.push((k.to_string(), v.to_string())),
|
||||
None => cfg_atoms.push(entry.to_string()),
|
||||
Some((k, v)) => cfgs.push((k.to_string(), Some(v.to_string()))),
|
||||
None => cfgs.push((entry.to_string(), None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -243,11 +302,11 @@ fn parse_meta_line(meta: &str) -> Fixture {
|
||||
krate,
|
||||
deps,
|
||||
extern_prelude,
|
||||
cfg_atoms,
|
||||
cfg_key_values,
|
||||
cfgs,
|
||||
edition,
|
||||
env,
|
||||
introduce_new_source_root,
|
||||
library,
|
||||
target_data_layout,
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user