diff --git a/Cargo.lock b/Cargo.lock index 0bc50f5e80..b78ab214d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1175,6 +1175,7 @@ dependencies = [ "base_db", "cargo_metadata", "cfg", + "expect-test", "la-arena", "log", "paths", diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 9a4baa6369..6ba056f308 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs @@ -23,11 +23,26 @@ pub use dnf::DnfExpr; /// of key and value in `key_values`. /// /// See: -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Clone, PartialEq, Eq, Default)] pub struct CfgOptions { enabled: FxHashSet, } +impl fmt::Debug for CfgOptions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut items = self + .enabled + .iter() + .map(|atom| match atom { + CfgAtom::Flag(it) => it.to_string(), + CfgAtom::KeyValue { key, value } => format!("{}={}", key, value), + }) + .collect::>(); + items.sort(); + f.debug_tuple("CfgOptions").field(&items).finish() + } +} + impl CfgOptions { pub fn check(&self, cfg: &CfgExpr) -> Option { cfg.fold(&|atom| self.enabled.contains(atom)) diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml index 5c85bc19c3..36ca83333d 100644 --- a/crates/project_model/Cargo.toml +++ b/crates/project_model/Cargo.toml @@ -16,6 +16,7 @@ semver = "1" serde = { version = "1.0.106", features = ["derive"] } serde_json = "1.0.48" anyhow = "1.0.26" +expect-test = "1" la-arena = { version = "0.2.0", path = "../../lib/arena" } cfg = { path = "../cfg", version = "0.0.0" } diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs index b0214c3a07..09bf8bce69 100644 --- a/crates/project_model/src/lib.rs +++ b/crates/project_model/src/lib.rs @@ -24,6 +24,9 @@ mod workspace; mod rustc_cfg; mod build_scripts; +#[cfg(test)] +mod tests; + use std::{ convert::{TryFrom, TryInto}, fs::{self, read_dir, ReadDir}, diff --git a/crates/project_model/src/tests.rs b/crates/project_model/src/tests.rs new file mode 100644 index 0000000000..302a000d70 --- /dev/null +++ b/crates/project_model/src/tests.rs @@ -0,0 +1,516 @@ +use std::path::PathBuf; + +use base_db::FileId; +use expect_test::{expect, Expect}; + +use crate::{CargoWorkspace, CfgOverrides, ProjectWorkspace, Sysroot, WorkspaceBuildScripts}; + +fn check(file: &str, expect: Expect) { + let meta = get_test_metadata(file); + let cargo_workspace = CargoWorkspace::new(meta); + let project_workspace = ProjectWorkspace::Cargo { + cargo: cargo_workspace, + build_scripts: WorkspaceBuildScripts::default(), + sysroot: Sysroot::default(), + rustc: None, + rustc_cfg: Vec::new(), + cfg_overrides: CfgOverrides::default(), + }; + + let crate_graph = project_workspace.to_crate_graph(None, { + let mut counter = 0; + &mut move |_path| { + counter += 1; + Some(FileId(counter)) + } + }); + + let mut crate_graph = format!("{:#?}", crate_graph); + replace_root(&mut crate_graph, false); + + expect.assert_eq(&crate_graph); +} + +fn get_test_metadata(file: &str) -> cargo_metadata::Metadata { + let mut json = get_test_data(file).parse::().unwrap(); + fixup_paths(&mut json); + return serde_json::from_value(json).unwrap(); + + fn fixup_paths(val: &mut serde_json::Value) -> () { + match val { + serde_json::Value::String(s) => replace_root(s, true), + serde_json::Value::Array(vals) => vals.iter_mut().for_each(fixup_paths), + serde_json::Value::Object(kvals) => kvals.values_mut().for_each(fixup_paths), + serde_json::Value::Null | serde_json::Value::Bool(_) | serde_json::Value::Number(_) => { + () + } + } + } +} + +fn replace_root(s: &mut String, direction: bool) { + if direction { + let root = if cfg!(windows) { r#"C:\\ROOT\"# } else { "/ROOT/" }; + *s = s.replace("$ROOT$", root) + } else { + let root = if cfg!(windows) { r#"C:\\\\ROOT\\"# } else { "/ROOT/" }; + *s = s.replace(root, "$ROOT$") + } +} + +fn get_test_data(file: &str) -> String { + let base = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let file = base.join("test_data").join(file); + std::fs::read_to_string(file).unwrap() +} + +#[test] +fn hello_world_project_model() { + check( + "hello-world-metadata.json", + expect![[r#" + CrateGraph { + arena: { + CrateId( + 0, + ): CrateData { + root_file_id: FileId( + 1, + ), + edition: Edition2018, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "hello_world", + ), + canonical_name: "hello-world", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + potential_cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: CrateId( + 4, + ), + name: CrateName( + "libc", + ), + }, + ], + proc_macro: [], + }, + CrateId( + 5, + ): CrateData { + root_file_id: FileId( + 6, + ), + edition: Edition2015, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "const_fn", + ), + canonical_name: "const_fn", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "feature=default", + "feature=std", + "test", + ], + ), + potential_cfg_options: CfgOptions( + [ + "debug_assertions", + "feature=align", + "feature=const-extern-fn", + "feature=default", + "feature=extra_traits", + "feature=rustc-dep-of-std", + "feature=std", + "feature=use_std", + "test", + ], + ), + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_PKG_VERSION": "0.2.98", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "libc", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "libc", + "CARGO_PKG_VERSION_PATCH": "98", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "2", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: CrateId( + 4, + ), + name: CrateName( + "libc", + ), + }, + ], + proc_macro: [], + }, + CrateId( + 2, + ): CrateData { + root_file_id: FileId( + 3, + ), + edition: Edition2018, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "an_example", + ), + canonical_name: "an-example", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + potential_cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: CrateId( + 0, + ), + name: CrateName( + "hello_world", + ), + }, + Dependency { + crate_id: CrateId( + 4, + ), + name: CrateName( + "libc", + ), + }, + ], + proc_macro: [], + }, + CrateId( + 4, + ): CrateData { + root_file_id: FileId( + 5, + ), + edition: Edition2015, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "libc", + ), + canonical_name: "libc", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "feature=default", + "feature=std", + "test", + ], + ), + potential_cfg_options: CfgOptions( + [ + "debug_assertions", + "feature=align", + "feature=const-extern-fn", + "feature=default", + "feature=extra_traits", + "feature=rustc-dep-of-std", + "feature=std", + "feature=use_std", + "test", + ], + ), + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_PKG_VERSION": "0.2.98", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "libc", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "libc", + "CARGO_PKG_VERSION_PATCH": "98", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "2", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [], + proc_macro: [], + }, + CrateId( + 1, + ): CrateData { + root_file_id: FileId( + 2, + ), + edition: Edition2018, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "hello_world", + ), + canonical_name: "hello-world", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + potential_cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: CrateId( + 0, + ), + name: CrateName( + "hello_world", + ), + }, + Dependency { + crate_id: CrateId( + 4, + ), + name: CrateName( + "libc", + ), + }, + ], + proc_macro: [], + }, + CrateId( + 6, + ): CrateData { + root_file_id: FileId( + 7, + ), + edition: Edition2015, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "build_script_build", + ), + canonical_name: "build-script-build", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "feature=default", + "feature=std", + "test", + ], + ), + potential_cfg_options: CfgOptions( + [ + "debug_assertions", + "feature=align", + "feature=const-extern-fn", + "feature=default", + "feature=extra_traits", + "feature=rustc-dep-of-std", + "feature=std", + "feature=use_std", + "test", + ], + ), + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_PKG_VERSION": "0.2.98", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "libc", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "libc", + "CARGO_PKG_VERSION_PATCH": "98", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "2", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [], + proc_macro: [], + }, + CrateId( + 3, + ): CrateData { + root_file_id: FileId( + 4, + ), + edition: Edition2018, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "it", + ), + canonical_name: "it", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + potential_cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: CrateId( + 0, + ), + name: CrateName( + "hello_world", + ), + }, + Dependency { + crate_id: CrateId( + 4, + ), + name: CrateName( + "libc", + ), + }, + ], + proc_macro: [], + }, + }, + }"#]], + ) +} diff --git a/crates/project_model/test_data/hello-world-metadata.json b/crates/project_model/test_data/hello-world-metadata.json new file mode 100644 index 0000000000..b6142eeaf2 --- /dev/null +++ b/crates/project_model/test_data/hello-world-metadata.json @@ -0,0 +1,245 @@ +{ + "packages": [ + { + "name": "hello-world", + "version": "0.1.0", + "id": "hello-world 0.1.0 (path+file://$ROOT$hello-world)", + "license": null, + "license_file": null, + "description": null, + "source": null, + "dependencies": [ + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "hello-world", + "src_path": "$ROOT$hello-world/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "hello-world", + "src_path": "$ROOT$hello-world/src/main.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "an-example", + "src_path": "$ROOT$hello-world/examples/an-example.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "it", + "src_path": "$ROOT$hello-world/tests/it.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$hello-world/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "keywords": [], + "readme": null, + "repository": null, + "homepage": null, + "documentation": null, + "edition": "2018", + "links": null + }, + { + "name": "libc", + "version": "0.2.98", + "id": "libc 0.2.98 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Raw FFI bindings to platform libraries like libc.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "libc", + "src_path": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "const_fn", + "src_path": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/tests/const_fn.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "align": [], + "const-extern-fn": [], + "default": [ + "std" + ], + "extra_traits": [], + "rustc-dep-of-std": [ + "align", + "rustc-std-workspace-core" + ], + "std": [], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "external-ffi-bindings", + "no-std", + "os" + ], + "keywords": [ + "libc", + "ffi", + "bindings", + "operating", + "system" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/libc", + "homepage": "https://github.com/rust-lang/libc", + "documentation": "https://docs.rs/libc/", + "edition": "2015", + "links": null + } + ], + "workspace_members": [ + "hello-world 0.1.0 (path+file://$ROOT$hello-world)" + ], + "resolve": { + "nodes": [ + { + "id": "hello-world 0.1.0 (path+file://$ROOT$hello-world)", + "dependencies": [ + "libc 0.2.98 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.98 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "libc 0.2.98 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + } + ], + "root": "hello-world 0.1.0 (path+file://$ROOT$hello-world)" + }, + "target_directory": "$ROOT$hello-world/target", + "version": 1, + "workspace_root": "$ROOT$hello-world", + "metadata": null +}