Rollup merge of #130236 - yaahc:unstable-feature-usage, r=estebank
unstable feature usage metrics example output ``` test-lib on master [?] is 📦 v0.1.0 via 🦀 v1.80.1 ❯ cat src/lib.rs ───────┬─────────────────────────────────────────────────────── │ File: src/lib.rs ───────┼─────────────────────────────────────────────────────── 1 │ #![feature(unix_set_mark)] 2 │ pub fn add(left: u64, right: u64) -> u64 { 3 │ left + right 4 │ } 5 │ 6 │ #[cfg(test)] 7 │ mod tests { 8 │ use super::*; 9 │ 10 │ #[test] 11 │ fn it_works() { 12 │ let result = add(2, 2); 13 │ assert_eq!(result, 4); 14 │ } 15 │ } ───────┴─────────────────────────────────────────────────────── test-lib on master [?] is 📦 v0.1.0 via 🦀 v1.80.1 ❯ cargo +stage1 rustc -- -Zmetrics-dir=$PWD/metrics Compiling test-lib v0.1.0 (/home/yaahc/tmp/test-lib) Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s test-lib on master [?] is 📦 v0.1.0 via 🦀 v1.80.1 ❯ cat metrics/unstable_feature_usage.json ───────┬───────────────────────────────────────────────────────────────────── │ File: metrics/unstable_feature_usage.json ───────┼───────────────────────────────────────────────────────────────────── 1 │ {"lib_features":[{"symbol":"unix_set_mark"}],"lang_features":[]} ``` related to https://github.com/rust-lang/rust/issues/129485
This commit is contained in:
commit
fe5403f517
10 changed files with 185 additions and 3 deletions
|
@ -3685,6 +3685,8 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"rustc_data_structures",
|
||||
"rustc_span",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -23,3 +23,5 @@ driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc ver
|
|||
driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}`
|
||||
|
||||
driver_impl_rlink_wrong_file_type = The input does not look like a .rlink file
|
||||
|
||||
driver_impl_unstable_feature_usage = cannot dump feature usage metrics: {$error}
|
||||
|
|
|
@ -51,6 +51,7 @@ use rustc_interface::{Linker, Queries, interface, passes};
|
|||
use rustc_lint::unerased_lint_store;
|
||||
use rustc_metadata::creader::MetadataLoader;
|
||||
use rustc_metadata::locator;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
|
||||
use rustc_session::config::{
|
||||
CG_OPTIONS, ErrorOutputType, Input, OutFileName, OutputType, UnstableOptions, Z_OPTIONS,
|
||||
|
@ -103,7 +104,7 @@ mod signal_handler {
|
|||
|
||||
use crate::session_diagnostics::{
|
||||
RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch,
|
||||
RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead,
|
||||
RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, UnstableFeatureUsage,
|
||||
};
|
||||
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
@ -431,6 +432,10 @@ fn run_compiler(
|
|||
// Make sure name resolution and macro expansion is run.
|
||||
queries.global_ctxt()?.enter(|tcx| tcx.resolver_for_lowering());
|
||||
|
||||
if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
|
||||
queries.global_ctxt()?.enter(|tcxt| dump_feature_usage_metrics(tcxt, metrics_dir));
|
||||
}
|
||||
|
||||
if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
|
||||
return early_exit();
|
||||
}
|
||||
|
@ -475,6 +480,23 @@ fn run_compiler(
|
|||
})
|
||||
}
|
||||
|
||||
fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &PathBuf) {
|
||||
let output_filenames = tcxt.output_filenames(());
|
||||
let mut metrics_file_name = std::ffi::OsString::from("unstable_feature_usage_metrics-");
|
||||
let mut metrics_path = output_filenames.with_directory_and_extension(metrics_dir, "json");
|
||||
let metrics_file_stem =
|
||||
metrics_path.file_name().expect("there should be a valid default output filename");
|
||||
metrics_file_name.push(metrics_file_stem);
|
||||
metrics_path.pop();
|
||||
metrics_path.push(metrics_file_name);
|
||||
if let Err(error) = tcxt.features().dump_feature_usage_metrics(metrics_path) {
|
||||
// FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit
|
||||
// default metrics" to only produce a warning when metrics are enabled by default and emit
|
||||
// an error only when the user manually enables metrics
|
||||
tcxt.dcx().emit_err(UnstableFeatureUsage { error });
|
||||
}
|
||||
}
|
||||
|
||||
// Extract output directory and file from matches.
|
||||
fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<OutFileName>) {
|
||||
let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o));
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::error::Error;
|
||||
|
||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
@ -93,3 +95,9 @@ pub(crate) struct IceFlags {
|
|||
#[derive(Diagnostic)]
|
||||
#[diag(driver_impl_ice_exclude_cargo_defaults)]
|
||||
pub(crate) struct IceExcludeCargoDefaults;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(driver_impl_unstable_feature_usage)]
|
||||
pub(crate) struct UnstableFeatureUsage {
|
||||
pub error: Box<dyn Error>,
|
||||
}
|
||||
|
|
|
@ -7,4 +7,6 @@ edition = "2021"
|
|||
# tidy-alphabetical-start
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
serde = { version = "1.0.125", features = [ "derive" ] }
|
||||
serde_json = "1.0.59"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! List of the unstable feature gates.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::{Symbol, sym};
|
||||
|
@ -651,6 +653,54 @@ declare_features! (
|
|||
// -------------------------------------------------------------------------
|
||||
);
|
||||
|
||||
impl Features {
|
||||
pub fn dump_feature_usage_metrics(
|
||||
&self,
|
||||
metrics_path: PathBuf,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[derive(serde::Serialize)]
|
||||
struct LibFeature {
|
||||
symbol: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct LangFeature {
|
||||
symbol: String,
|
||||
since: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct FeatureUsage {
|
||||
lib_features: Vec<LibFeature>,
|
||||
lang_features: Vec<LangFeature>,
|
||||
}
|
||||
|
||||
let metrics_file = std::fs::File::create(metrics_path)?;
|
||||
let metrics_file = std::io::BufWriter::new(metrics_file);
|
||||
|
||||
let lib_features = self
|
||||
.enabled_lib_features
|
||||
.iter()
|
||||
.map(|EnabledLibFeature { gate_name, .. }| LibFeature { symbol: gate_name.to_string() })
|
||||
.collect();
|
||||
|
||||
let lang_features = self
|
||||
.enabled_lang_features
|
||||
.iter()
|
||||
.map(|EnabledLangFeature { gate_name, stable_since, .. }| LangFeature {
|
||||
symbol: gate_name.to_string(),
|
||||
since: stable_since.map(|since| since.to_string()),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let feature_usage = FeatureUsage { lib_features, lang_features };
|
||||
|
||||
serde_json::to_writer(metrics_file, &feature_usage)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Some features are not allowed to be used together at the same time, if
|
||||
/// the two are present, produce an error.
|
||||
///
|
||||
|
|
|
@ -1069,7 +1069,7 @@ impl OutputFilenames {
|
|||
self.with_directory_and_extension(&self.out_directory, extension)
|
||||
}
|
||||
|
||||
fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf {
|
||||
pub fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf {
|
||||
let mut path = directory.join(&self.filestem);
|
||||
path.set_extension(extension);
|
||||
path
|
||||
|
|
|
@ -1887,7 +1887,7 @@ options! {
|
|||
meta_stats: bool = (false, parse_bool, [UNTRACKED],
|
||||
"gather metadata statistics (default: no)"),
|
||||
metrics_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
|
||||
"stores metrics about the errors being emitted by rustc to disk"),
|
||||
"the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"),
|
||||
mir_emit_retag: bool = (false, parse_bool, [TRACKED],
|
||||
"emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
|
||||
(default: no)"),
|
||||
|
|
9
tests/run-make/unstable-feature-usage-metrics/lib.rs
Normal file
9
tests/run-make/unstable-feature-usage-metrics/lib.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
#![feature(ascii_char)] // random lib feature
|
||||
#![feature(box_patterns)] // random lang feature
|
||||
|
||||
// picked arbitrary unstable features, just need a random lib and lang feature, ideally ones that
|
||||
// won't be stabilized any time soon so we don't have to update this test
|
||||
|
||||
fn main() {
|
||||
println!("foobar");
|
||||
}
|
87
tests/run-make/unstable-feature-usage-metrics/rmake.rs
Normal file
87
tests/run-make/unstable-feature-usage-metrics/rmake.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
//! This test checks if unstable feature usage metric dump files `unstable-feature-usage*.json` work
|
||||
//! as expected.
|
||||
//!
|
||||
//! - Basic sanity checks on a default ICE dump.
|
||||
//!
|
||||
//! See <https://github.com/rust-lang/rust/issues/129485>.
|
||||
//!
|
||||
//! # Test history
|
||||
//!
|
||||
//! - forked from dump-ice-to-disk test, which has flakeyness issues on i686-mingw, I'm assuming
|
||||
//! those will be present in this test as well on the same platform
|
||||
|
||||
//@ ignore-windows
|
||||
//FIXME(#128911): still flakey on i686-mingw.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use run_make_support::rfs::create_dir_all;
|
||||
use run_make_support::{
|
||||
cwd, filename_contains, has_extension, rfs, run_in_tmpdir, rustc, serde_json,
|
||||
shallow_find_files,
|
||||
};
|
||||
|
||||
fn find_feature_usage_metrics<P: AsRef<Path>>(dir: P) -> Vec<PathBuf> {
|
||||
shallow_find_files(dir, |path| {
|
||||
if filename_contains(path, "unstable_feature_usage") && has_extension(path, "json") {
|
||||
true
|
||||
} else {
|
||||
dbg!(path);
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_metrics_dump();
|
||||
test_metrics_errors();
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn test_metrics_dump() {
|
||||
run_in_tmpdir(|| {
|
||||
let metrics_dir = cwd().join("metrics");
|
||||
create_dir_all(&metrics_dir);
|
||||
rustc()
|
||||
.input("lib.rs")
|
||||
.env("RUST_BACKTRACE", "short")
|
||||
.arg(format!("-Zmetrics-dir={}", metrics_dir.display()))
|
||||
.run();
|
||||
let mut metrics = find_feature_usage_metrics(&metrics_dir);
|
||||
let json_path =
|
||||
metrics.pop().expect("there should be one metrics file in the output directory");
|
||||
|
||||
// After the `pop` above, there should be no files left.
|
||||
assert!(
|
||||
metrics.is_empty(),
|
||||
"there should be no more than one metrics file in the output directory"
|
||||
);
|
||||
|
||||
let message = rfs::read_to_string(json_path);
|
||||
let parsed: serde_json::Value =
|
||||
serde_json::from_str(&message).expect("metrics should be dumped as json");
|
||||
let expected = serde_json::json!(
|
||||
{
|
||||
"lib_features":[{"symbol":"ascii_char"}],
|
||||
"lang_features":[{"symbol":"box_patterns","since":null}]
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(expected, parsed);
|
||||
});
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn test_metrics_errors() {
|
||||
run_in_tmpdir(|| {
|
||||
rustc()
|
||||
.input("lib.rs")
|
||||
.env("RUST_BACKTRACE", "short")
|
||||
.arg("-Zmetrics-dir=invaliddirectorythatdefinitelydoesntexist")
|
||||
.run_fail()
|
||||
.assert_stderr_contains(
|
||||
"error: cannot dump feature usage metrics: No such file or directory",
|
||||
)
|
||||
.assert_stdout_not_contains("internal compiler error");
|
||||
});
|
||||
}
|
Loading…
Add table
Reference in a new issue