Rollup merge of #93915 - Urgau:rfc-3013, r=petrochenkov
Implement --check-cfg option (RFC 3013), take 2 This pull-request implement RFC 3013: Checking conditional compilation at compile time (https://github.com/rust-lang/rfcs/pull/3013) and is based on the previous attempt https://github.com/rust-lang/rust/pull/89346 by `@mwkmwkmwk` that was closed due to inactivity. I have address all the review comments from the previous attempt and added some more tests. cc https://github.com/rust-lang/rust/issues/82450 r? `@petrochenkov`
This commit is contained in:
commit
576afec73a
27 changed files with 365 additions and 7 deletions
|
@ -2,7 +2,7 @@ pub use crate::passes::BoxedResolver;
|
|||
use crate::util;
|
||||
|
||||
use rustc_ast::token;
|
||||
use rustc_ast::{self as ast, MetaItemKind};
|
||||
use rustc_ast::{self as ast, LitKind, MetaItemKind};
|
||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
@ -13,12 +13,13 @@ use rustc_lint::LintStore;
|
|||
use rustc_middle::ty;
|
||||
use rustc_parse::maybe_new_parser_from_source_str;
|
||||
use rustc_query_impl::QueryCtxt;
|
||||
use rustc_session::config::{self, ErrorOutputType, Input, OutputFilenames};
|
||||
use rustc_session::config::{self, CheckCfg, ErrorOutputType, Input, OutputFilenames};
|
||||
use rustc_session::early_error;
|
||||
use rustc_session::lint;
|
||||
use rustc_session::parse::{CrateConfig, ParseSess};
|
||||
use rustc_session::{DiagnosticOutput, Session};
|
||||
use rustc_span::source_map::{FileLoader, FileName};
|
||||
use rustc_span::symbol::sym;
|
||||
use std::path::PathBuf;
|
||||
use std::result;
|
||||
|
||||
|
@ -139,6 +140,90 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
|
|||
})
|
||||
}
|
||||
|
||||
/// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
|
||||
pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
|
||||
rustc_span::create_default_session_if_not_set_then(move |_| {
|
||||
let mut cfg = CheckCfg::default();
|
||||
|
||||
'specs: for s in specs {
|
||||
let sess = ParseSess::with_silent_emitter(Some(format!(
|
||||
"this error occurred on the command line: `--check-cfg={}`",
|
||||
s
|
||||
)));
|
||||
let filename = FileName::cfg_spec_source_code(&s);
|
||||
|
||||
macro_rules! error {
|
||||
($reason: expr) => {
|
||||
early_error(
|
||||
ErrorOutputType::default(),
|
||||
&format!(
|
||||
concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
|
||||
s
|
||||
),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
|
||||
Ok(mut parser) => match &mut parser.parse_meta_item() {
|
||||
Ok(meta_item) if parser.token == token::Eof => {
|
||||
if let Some(args) = meta_item.meta_item_list() {
|
||||
if meta_item.has_name(sym::names) {
|
||||
cfg.names_checked = true;
|
||||
for arg in args {
|
||||
if arg.is_word() && arg.ident().is_some() {
|
||||
let ident = arg.ident().expect("multi-segment cfg key");
|
||||
cfg.names_valid.insert(ident.name.to_string());
|
||||
} else {
|
||||
error!("`names()` arguments must be simple identifers");
|
||||
}
|
||||
}
|
||||
continue 'specs;
|
||||
} else if meta_item.has_name(sym::values) {
|
||||
if let Some((name, values)) = args.split_first() {
|
||||
if name.is_word() && name.ident().is_some() {
|
||||
let ident = name.ident().expect("multi-segment cfg key");
|
||||
cfg.values_checked.insert(ident.to_string());
|
||||
for val in values {
|
||||
if let Some(LitKind::Str(s, _)) =
|
||||
val.literal().map(|lit| &lit.kind)
|
||||
{
|
||||
cfg.values_valid
|
||||
.insert((ident.to_string(), s.to_string()));
|
||||
} else {
|
||||
error!(
|
||||
"`values()` arguments must be string literals"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
continue 'specs;
|
||||
} else {
|
||||
error!(
|
||||
"`values()` first argument must be a simple identifer"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(..) => {}
|
||||
Err(err) => err.cancel(),
|
||||
},
|
||||
Err(errs) => errs.into_iter().for_each(|mut err| err.cancel()),
|
||||
}
|
||||
|
||||
error!(
|
||||
"expected `names(name1, name2, ... nameN)` or \
|
||||
`values(name, \"value1\", \"value2\", ... \"valueN\")`"
|
||||
);
|
||||
}
|
||||
|
||||
cfg.names_valid.extend(cfg.values_checked.iter().cloned());
|
||||
cfg
|
||||
})
|
||||
}
|
||||
|
||||
/// The compiler configuration
|
||||
pub struct Config {
|
||||
/// Command line options
|
||||
|
@ -146,6 +231,7 @@ pub struct Config {
|
|||
|
||||
/// cfg! configuration in addition to the default ones
|
||||
pub crate_cfg: FxHashSet<(String, Option<String>)>,
|
||||
pub crate_check_cfg: CheckCfg,
|
||||
|
||||
pub input: Input,
|
||||
pub input_path: Option<PathBuf>,
|
||||
|
@ -188,6 +274,7 @@ pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R
|
|||
let (mut sess, codegen_backend) = util::create_session(
|
||||
config.opts,
|
||||
config.crate_cfg,
|
||||
config.crate_check_cfg,
|
||||
config.diagnostic_output,
|
||||
config.file_loader,
|
||||
config.input_path.clone(),
|
||||
|
|
|
@ -15,6 +15,7 @@ use rustc_parse::validate_attr;
|
|||
use rustc_query_impl::QueryCtxt;
|
||||
use rustc_resolve::{self, Resolver};
|
||||
use rustc_session as session;
|
||||
use rustc_session::config::CheckCfg;
|
||||
use rustc_session::config::{self, CrateType};
|
||||
use rustc_session::config::{ErrorOutputType, Input, OutputFilenames};
|
||||
use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
|
||||
|
@ -65,6 +66,7 @@ pub fn add_configuration(
|
|||
pub fn create_session(
|
||||
sopts: config::Options,
|
||||
cfg: FxHashSet<(String, Option<String>)>,
|
||||
check_cfg: CheckCfg,
|
||||
diagnostic_output: DiagnosticOutput,
|
||||
file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
|
||||
input_path: Option<PathBuf>,
|
||||
|
@ -100,7 +102,13 @@ pub fn create_session(
|
|||
|
||||
let mut cfg = config::build_configuration(&sess, config::to_crate_config(cfg));
|
||||
add_configuration(&mut cfg, &mut sess, &*codegen_backend);
|
||||
|
||||
let mut check_cfg = config::to_crate_check_config(check_cfg);
|
||||
check_cfg.fill_well_known();
|
||||
check_cfg.fill_actual(&cfg);
|
||||
|
||||
sess.parse_sess.config = cfg;
|
||||
sess.parse_sess.check_config = check_cfg;
|
||||
|
||||
(Lrc::new(sess), Lrc::new(codegen_backend))
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue