X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=tests%2Fcompile-test.rs;h=a2d58491872b30813e99e0bb2685478183c6dc74;hb=97ab44ca974544fdf5ef598b04d3794af513e4bf;hp=caa19e39ccd3af1baad8a61ad79688cabee02c8d;hpb=ebe52869a344ca11263934c208dc18412acd794b;p=rust.git diff --git a/tests/compile-test.rs b/tests/compile-test.rs index caa19e39ccd..a2d58491872 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,9 +1,11 @@ #![feature(test)] // compiletest_rs requires this attribute -#![feature(once_cell)] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] use compiletest_rs as compiletest; use compiletest_rs::common::Mode as TestMode; +use std::collections::HashMap; use std::env::{self, remove_var, set_var, var_os}; use std::ffi::{OsStr, OsString}; use std::fs; @@ -15,73 +17,100 @@ // whether to run internal tests or not const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints"); -fn host_lib() -> PathBuf { - option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from) -} - -fn clippy_driver_path() -> PathBuf { - option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from) -} - -// When we'll want to use `extern crate ..` for a dependency that is used -// both by the crate and the compiler itself, we can't simply pass -L flags -// as we'll get a duplicate matching versions. Instead, disambiguate with -// `--extern dep=path`. -// See https://github.com/rust-lang/rust-clippy/issues/4015. -// -// FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files. -// Because it would force-rebuild if the options passed to `build` command is not the same -// as what we manually pass to `cargo` invocation -fn third_party_crates() -> String { - use std::collections::HashMap; - static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints", "syn", "quote"]; - let dep_dir = cargo::TARGET_LIB.join("deps"); - let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len()); - for entry in fs::read_dir(dep_dir).unwrap() { - let path = match entry { - Ok(entry) => entry.path(), - Err(_) => continue, +/// All crates used in UI tests are listed here +static TEST_DEPENDENCIES: &[&str] = &[ + "clippy_utils", + "derive_new", + "if_chain", + "itertools", + "quote", + "regex", + "serde", + "serde_derive", + "syn", + "parking_lot", +]; + +// Test dependencies may need an `extern crate` here to ensure that they show up +// in the depinfo file (otherwise cargo thinks they are unused) +#[allow(unused_extern_crates)] +extern crate clippy_utils; +#[allow(unused_extern_crates)] +extern crate derive_new; +#[allow(unused_extern_crates)] +extern crate if_chain; +#[allow(unused_extern_crates)] +extern crate itertools; +#[allow(unused_extern_crates)] +extern crate parking_lot; +#[allow(unused_extern_crates)] +extern crate quote; +#[allow(unused_extern_crates)] +extern crate syn; + +/// Produces a string with an `--extern` flag for all UI test crate +/// dependencies. +/// +/// The dependency files are located by parsing the depinfo file for this test +/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test +/// dependencies must be added to Cargo.toml at the project root. Test +/// dependencies that are not *directly* used by this test module require an +/// `extern crate` declaration. +fn extern_flags() -> String { + let current_exe_depinfo = { + let mut path = env::current_exe().unwrap(); + path.set_extension("d"); + std::fs::read_to_string(path).unwrap() + }; + let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len()); + for line in current_exe_depinfo.lines() { + // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:` + let parse_name_path = || { + if line.starts_with(char::is_whitespace) { + return None; + } + let path_str = line.strip_suffix(':')?; + let path = Path::new(path_str); + if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") { + return None; + } + let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?; + // the "lib" prefix is not present for dll files + let name = name.strip_prefix("lib").unwrap_or(name); + Some((name, path_str)) }; - if let Some(name) = path.file_name().and_then(OsStr::to_str) { - for dep in CRATES { - if name.starts_with(&format!("lib{}-", dep)) - && name.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rlib")) == Some(true) - { - if let Some(old) = crates.insert(dep, path.clone()) { - // Check which action should be done in order to remove compiled deps. - // If pre-installed version of compiler is used, `cargo clean` will do. - // Otherwise (for bootstrapped compiler), the dependencies directory - // must be removed manually. - let suggested_action = if std::env::var_os("RUSTC_BOOTSTRAP").is_some() { - "remove the stageN-tools directory" - } else { - "run `cargo clean`" - }; - - panic!( - "\n---------------------------------------------------\n\n \ - Found multiple rlibs for crate `{}`: `{:?}` and `{:?}`.\n \ - Probably, you need to {} before running tests again.\n \ - \nFor details on that error see https://github.com/rust-lang/rust-clippy/issues/7343 \ - \n---------------------------------------------------\n", - dep, old, path, suggested_action - ); - } - break; - } + if let Some((name, path)) = parse_name_path() { + if TEST_DEPENDENCIES.contains(&name) { + // A dependency may be listed twice if it is available in sysroot, + // and the sysroot dependencies are listed first. As of the writing, + // this only seems to apply to if_chain. + crates.insert(name, path); } } } - - let v: Vec<_> = crates - .into_iter() - .map(|(dep, path)| format!("--extern {}={}", dep, path.display())) + let not_found: Vec<&str> = TEST_DEPENDENCIES + .iter() + .copied() + .filter(|n| !crates.contains_key(n)) .collect(); - v.join(" ") + assert!( + not_found.is_empty(), + "dependencies not found in depinfo: {:?}\n\ + help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\ + help: Try adding to dev-dependencies in Cargo.toml", + not_found + ); + crates + .into_iter() + .map(|(name, path)| format!(" --extern {}={}", name, path)) + .collect() } fn default_config() -> compiletest::Config { - let mut config = compiletest::Config::default(); + let mut config = compiletest::Config { + edition: Some("2021".into()), + ..compiletest::Config::default() + }; if let Ok(filters) = env::var("TESTNAME") { config.filters = filters.split(',').map(std::string::ToString::to_string).collect(); @@ -92,16 +121,29 @@ fn default_config() -> compiletest::Config { config.run_lib_path = path.clone(); config.compile_lib_path = path; } - + let current_exe_path = std::env::current_exe().unwrap(); + let deps_path = current_exe_path.parent().unwrap(); + let profile_path = deps_path.parent().unwrap(); + + // Using `-L dependency={}` enforces that external dependencies are added with `--extern`. + // This is valuable because a) it allows us to monitor what external dependencies are used + // and b) it ensures that conflicting rlibs are resolved properly. + let host_libs = option_env!("HOST_LIBS") + .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display())) + .unwrap_or_default(); config.target_rustcflags = Some(format!( - "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}", - host_lib().join("deps").display(), - cargo::TARGET_LIB.join("deps").display(), - third_party_crates(), + "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}", + deps_path.display(), + host_libs, + extern_flags(), )); - config.build_base = host_lib().join("test_build_base"); - config.rustc_path = clippy_driver_path(); + config.build_base = profile_path.join("test"); + config.rustc_path = profile_path.join(if cfg!(windows) { + "clippy-driver.exe" + } else { + "clippy-driver" + }); config } @@ -113,6 +155,19 @@ fn run_ui(cfg: &mut compiletest::Config) { compiletest::run_tests(cfg); } +fn run_ui_test(cfg: &mut compiletest::Config) { + cfg.mode = TestMode::Ui; + cfg.src_base = Path::new("tests").join("ui_test"); + let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap()); + let rustcflags = cfg.target_rustcflags.get_or_insert_with(Default::default); + let len = rustcflags.len(); + rustcflags.push_str(" --test"); + compiletest::run_tests(cfg); + if let Some(ref mut flags) = &mut cfg.target_rustcflags { + flags.truncate(len); + } +} + fn run_internal_tests(cfg: &mut compiletest::Config) { // only run internal tests with the internal-tests feature if !RUN_INTERNAL_TESTS { @@ -276,6 +331,7 @@ fn compile_test() { prepare_env(); let mut config = default_config(); run_ui(&mut config); + run_ui_test(&mut config); run_ui_toml(&mut config); run_ui_cargo(&mut config); run_internal_tests(&mut config);