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
|
@ -1,10 +1,13 @@
|
||||||
//! Parsing and validation of builtin attributes
|
//! Parsing and validation of builtin attributes
|
||||||
|
|
||||||
use rustc_ast::{self as ast, Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem};
|
use rustc_ast as ast;
|
||||||
|
use rustc_ast::node_id::CRATE_NODE_ID;
|
||||||
|
use rustc_ast::{Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem};
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_errors::{struct_span_err, Applicability};
|
use rustc_errors::{struct_span_err, Applicability};
|
||||||
use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
|
use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
|
||||||
use rustc_macros::HashStable_Generic;
|
use rustc_macros::HashStable_Generic;
|
||||||
|
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
|
||||||
use rustc_session::parse::{feature_err, ParseSess};
|
use rustc_session::parse::{feature_err, ParseSess};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::hygiene::Transparency;
|
use rustc_span::hygiene::Transparency;
|
||||||
|
@ -458,8 +461,30 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
MetaItemKind::NameValue(..) | MetaItemKind::Word => {
|
MetaItemKind::NameValue(..) | MetaItemKind::Word => {
|
||||||
let ident = cfg.ident().expect("multi-segment cfg predicate");
|
let name = cfg.ident().expect("multi-segment cfg predicate").name;
|
||||||
sess.config.contains(&(ident.name, cfg.value_str()))
|
let value = cfg.value_str();
|
||||||
|
if sess.check_config.names_checked && !sess.check_config.names_valid.contains(&name)
|
||||||
|
{
|
||||||
|
sess.buffer_lint(
|
||||||
|
UNEXPECTED_CFGS,
|
||||||
|
cfg.span,
|
||||||
|
CRATE_NODE_ID,
|
||||||
|
"unexpected `cfg` condition name",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(val) = value {
|
||||||
|
if sess.check_config.values_checked.contains(&name)
|
||||||
|
&& !sess.check_config.values_valid.contains(&(name, val))
|
||||||
|
{
|
||||||
|
sess.buffer_lint(
|
||||||
|
UNEXPECTED_CFGS,
|
||||||
|
cfg.span,
|
||||||
|
CRATE_NODE_ID,
|
||||||
|
"unexpected `cfg` condition value",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sess.config.contains(&(name, value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -216,10 +216,12 @@ fn run_compiler(
|
||||||
}
|
}
|
||||||
|
|
||||||
let cfg = interface::parse_cfgspecs(matches.opt_strs("cfg"));
|
let cfg = interface::parse_cfgspecs(matches.opt_strs("cfg"));
|
||||||
|
let check_cfg = interface::parse_check_cfg(matches.opt_strs("check-cfg"));
|
||||||
let (odir, ofile) = make_output(&matches);
|
let (odir, ofile) = make_output(&matches);
|
||||||
let mut config = interface::Config {
|
let mut config = interface::Config {
|
||||||
opts: sopts,
|
opts: sopts,
|
||||||
crate_cfg: cfg,
|
crate_cfg: cfg,
|
||||||
|
crate_check_cfg: check_cfg,
|
||||||
input: Input::File(PathBuf::new()),
|
input: Input::File(PathBuf::new()),
|
||||||
input_path: None,
|
input_path: None,
|
||||||
output_file: ofile,
|
output_file: ofile,
|
||||||
|
|
|
@ -2,7 +2,7 @@ pub use crate::passes::BoxedResolver;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
use rustc_ast::token;
|
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_codegen_ssa::traits::CodegenBackend;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
|
@ -13,12 +13,13 @@ use rustc_lint::LintStore;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_parse::maybe_new_parser_from_source_str;
|
use rustc_parse::maybe_new_parser_from_source_str;
|
||||||
use rustc_query_impl::QueryCtxt;
|
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::early_error;
|
||||||
use rustc_session::lint;
|
use rustc_session::lint;
|
||||||
use rustc_session::parse::{CrateConfig, ParseSess};
|
use rustc_session::parse::{CrateConfig, ParseSess};
|
||||||
use rustc_session::{DiagnosticOutput, Session};
|
use rustc_session::{DiagnosticOutput, Session};
|
||||||
use rustc_span::source_map::{FileLoader, FileName};
|
use rustc_span::source_map::{FileLoader, FileName};
|
||||||
|
use rustc_span::symbol::sym;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::result;
|
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
|
/// The compiler configuration
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// Command line options
|
/// Command line options
|
||||||
|
@ -146,6 +231,7 @@ pub struct Config {
|
||||||
|
|
||||||
/// cfg! configuration in addition to the default ones
|
/// cfg! configuration in addition to the default ones
|
||||||
pub crate_cfg: FxHashSet<(String, Option<String>)>,
|
pub crate_cfg: FxHashSet<(String, Option<String>)>,
|
||||||
|
pub crate_check_cfg: CheckCfg,
|
||||||
|
|
||||||
pub input: Input,
|
pub input: Input,
|
||||||
pub input_path: Option<PathBuf>,
|
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(
|
let (mut sess, codegen_backend) = util::create_session(
|
||||||
config.opts,
|
config.opts,
|
||||||
config.crate_cfg,
|
config.crate_cfg,
|
||||||
|
config.crate_check_cfg,
|
||||||
config.diagnostic_output,
|
config.diagnostic_output,
|
||||||
config.file_loader,
|
config.file_loader,
|
||||||
config.input_path.clone(),
|
config.input_path.clone(),
|
||||||
|
|
|
@ -15,6 +15,7 @@ use rustc_parse::validate_attr;
|
||||||
use rustc_query_impl::QueryCtxt;
|
use rustc_query_impl::QueryCtxt;
|
||||||
use rustc_resolve::{self, Resolver};
|
use rustc_resolve::{self, Resolver};
|
||||||
use rustc_session as session;
|
use rustc_session as session;
|
||||||
|
use rustc_session::config::CheckCfg;
|
||||||
use rustc_session::config::{self, CrateType};
|
use rustc_session::config::{self, CrateType};
|
||||||
use rustc_session::config::{ErrorOutputType, Input, OutputFilenames};
|
use rustc_session::config::{ErrorOutputType, Input, OutputFilenames};
|
||||||
use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
|
use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
|
||||||
|
@ -65,6 +66,7 @@ pub fn add_configuration(
|
||||||
pub fn create_session(
|
pub fn create_session(
|
||||||
sopts: config::Options,
|
sopts: config::Options,
|
||||||
cfg: FxHashSet<(String, Option<String>)>,
|
cfg: FxHashSet<(String, Option<String>)>,
|
||||||
|
check_cfg: CheckCfg,
|
||||||
diagnostic_output: DiagnosticOutput,
|
diagnostic_output: DiagnosticOutput,
|
||||||
file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
|
file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
|
||||||
input_path: Option<PathBuf>,
|
input_path: Option<PathBuf>,
|
||||||
|
@ -100,7 +102,13 @@ pub fn create_session(
|
||||||
|
|
||||||
let mut cfg = config::build_configuration(&sess, config::to_crate_config(cfg));
|
let mut cfg = config::build_configuration(&sess, config::to_crate_config(cfg));
|
||||||
add_configuration(&mut cfg, &mut sess, &*codegen_backend);
|
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.config = cfg;
|
||||||
|
sess.parse_sess.check_config = check_cfg;
|
||||||
|
|
||||||
(Lrc::new(sess), Lrc::new(codegen_backend))
|
(Lrc::new(sess), Lrc::new(codegen_backend))
|
||||||
}
|
}
|
||||||
|
|
|
@ -2957,6 +2957,43 @@ declare_lint! {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
/// The `unexpected_cfgs` lint detects unexpected conditional compilation conditions.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// rustc --check-cfg 'names()'
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```rust,ignore (needs command line option)
|
||||||
|
/// #[cfg(widnows)]
|
||||||
|
/// fn foo() {}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This will produce:
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// warning: unknown condition name used
|
||||||
|
/// --> lint_example.rs:1:7
|
||||||
|
/// |
|
||||||
|
/// 1 | #[cfg(widnows)]
|
||||||
|
/// | ^^^^^^^
|
||||||
|
/// |
|
||||||
|
/// = note: `#[warn(unexpected_cfgs)]` on by default
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### Explanation
|
||||||
|
///
|
||||||
|
/// This lint is only active when a `--check-cfg='names(...)'` option has been passed
|
||||||
|
/// to the compiler and triggers whenever an unknown condition name or value is used.
|
||||||
|
/// The known condition include names or values passed in `--check-cfg`, `--cfg`, and some
|
||||||
|
/// well-knows names and values built into the compiler.
|
||||||
|
pub UNEXPECTED_CFGS,
|
||||||
|
Warn,
|
||||||
|
"detects unexpected names and values in `#[cfg]` conditions",
|
||||||
|
}
|
||||||
|
|
||||||
declare_lint_pass! {
|
declare_lint_pass! {
|
||||||
/// Does nothing as a lint pass, but registers some `Lint`s
|
/// Does nothing as a lint pass, but registers some `Lint`s
|
||||||
/// that are used by other parts of the compiler.
|
/// that are used by other parts of the compiler.
|
||||||
|
@ -3055,6 +3092,7 @@ declare_lint_pass! {
|
||||||
DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
|
DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
|
||||||
DUPLICATE_MACRO_ATTRIBUTES,
|
DUPLICATE_MACRO_ATTRIBUTES,
|
||||||
SUSPICIOUS_AUTO_TRAIT_IMPLS,
|
SUSPICIOUS_AUTO_TRAIT_IMPLS,
|
||||||
|
UNEXPECTED_CFGS,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ use rustc_target::spec::{LinkerFlavor, SplitDebuginfo, Target, TargetTriple, Tar
|
||||||
|
|
||||||
use rustc_serialize::json;
|
use rustc_serialize::json;
|
||||||
|
|
||||||
use crate::parse::CrateConfig;
|
use crate::parse::{CrateCheckConfig, CrateConfig};
|
||||||
use rustc_feature::UnstableFeatures;
|
use rustc_feature::UnstableFeatures;
|
||||||
use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST, LATEST_STABLE_EDITION};
|
use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST, LATEST_STABLE_EDITION};
|
||||||
use rustc_span::source_map::{FileName, FilePathMapping};
|
use rustc_span::source_map::{FileName, FilePathMapping};
|
||||||
|
@ -936,6 +936,7 @@ pub const fn default_lib_output() -> CrateType {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_configuration(sess: &Session) -> CrateConfig {
|
fn default_configuration(sess: &Session) -> CrateConfig {
|
||||||
|
// NOTE: This should be kept in sync with `CrateCheckConfig::fill_well_known` below.
|
||||||
let end = &sess.target.endian;
|
let end = &sess.target.endian;
|
||||||
let arch = &sess.target.arch;
|
let arch = &sess.target.arch;
|
||||||
let wordsz = sess.target.pointer_width.to_string();
|
let wordsz = sess.target.pointer_width.to_string();
|
||||||
|
@ -1020,6 +1021,91 @@ pub fn to_crate_config(cfg: FxHashSet<(String, Option<String>)>) -> CrateConfig
|
||||||
cfg.into_iter().map(|(a, b)| (Symbol::intern(&a), b.map(|b| Symbol::intern(&b)))).collect()
|
cfg.into_iter().map(|(a, b)| (Symbol::intern(&a), b.map(|b| Symbol::intern(&b)))).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The parsed `--check-cfg` options
|
||||||
|
pub struct CheckCfg<T = String> {
|
||||||
|
/// Set if `names()` checking is enabled
|
||||||
|
pub names_checked: bool,
|
||||||
|
/// The union of all `names()`
|
||||||
|
pub names_valid: FxHashSet<T>,
|
||||||
|
/// The set of names for which `values()` was used
|
||||||
|
pub values_checked: FxHashSet<T>,
|
||||||
|
/// The set of all (name, value) pairs passed in `values()`
|
||||||
|
pub values_valid: FxHashSet<(T, T)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for CheckCfg<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
CheckCfg {
|
||||||
|
names_checked: false,
|
||||||
|
names_valid: FxHashSet::default(),
|
||||||
|
values_checked: FxHashSet::default(),
|
||||||
|
values_valid: FxHashSet::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> CheckCfg<T> {
|
||||||
|
fn map_data<O: Eq + Hash>(&self, f: impl Fn(&T) -> O) -> CheckCfg<O> {
|
||||||
|
CheckCfg {
|
||||||
|
names_checked: self.names_checked,
|
||||||
|
names_valid: self.names_valid.iter().map(|a| f(a)).collect(),
|
||||||
|
values_checked: self.values_checked.iter().map(|a| f(a)).collect(),
|
||||||
|
values_valid: self.values_valid.iter().map(|(a, b)| (f(a), f(b))).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the crate `--check-cfg` options from `String` to `Symbol`.
|
||||||
|
/// `rustc_interface::interface::Config` accepts this in the compiler configuration,
|
||||||
|
/// but the symbol interner is not yet set up then, so we must convert it later.
|
||||||
|
pub fn to_crate_check_config(cfg: CheckCfg) -> CrateCheckConfig {
|
||||||
|
cfg.map_data(|s| Symbol::intern(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CrateCheckConfig {
|
||||||
|
/// Fills a `CrateCheckConfig` with well-known configuration names.
|
||||||
|
pub fn fill_well_known(&mut self) {
|
||||||
|
// NOTE: This should be kept in sync with `default_configuration`
|
||||||
|
const WELL_KNOWN_NAMES: &[Symbol] = &[
|
||||||
|
sym::unix,
|
||||||
|
sym::windows,
|
||||||
|
sym::target_os,
|
||||||
|
sym::target_family,
|
||||||
|
sym::target_arch,
|
||||||
|
sym::target_endian,
|
||||||
|
sym::target_pointer_width,
|
||||||
|
sym::target_env,
|
||||||
|
sym::target_abi,
|
||||||
|
sym::target_vendor,
|
||||||
|
sym::target_thread_local,
|
||||||
|
sym::target_has_atomic_load_store,
|
||||||
|
sym::target_has_atomic,
|
||||||
|
sym::target_has_atomic_equal_alignment,
|
||||||
|
sym::panic,
|
||||||
|
sym::sanitize,
|
||||||
|
sym::debug_assertions,
|
||||||
|
sym::proc_macro,
|
||||||
|
sym::test,
|
||||||
|
sym::doc,
|
||||||
|
sym::doctest,
|
||||||
|
sym::feature,
|
||||||
|
];
|
||||||
|
for &name in WELL_KNOWN_NAMES {
|
||||||
|
self.names_valid.insert(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fills a `CrateCheckConfig` with configuration names and values that are actually active.
|
||||||
|
pub fn fill_actual(&mut self, cfg: &CrateConfig) {
|
||||||
|
for &(k, v) in cfg {
|
||||||
|
self.names_valid.insert(k);
|
||||||
|
if let Some(v) = v {
|
||||||
|
self.values_valid.insert((k, v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig {
|
pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig {
|
||||||
// Combine the configuration requested by the session (command line) with
|
// Combine the configuration requested by the session (command line) with
|
||||||
// some default and generated configuration items.
|
// some default and generated configuration items.
|
||||||
|
@ -1163,6 +1249,7 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
|
||||||
vec![
|
vec![
|
||||||
opt::flag_s("h", "help", "Display this message"),
|
opt::flag_s("h", "help", "Display this message"),
|
||||||
opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"),
|
opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"),
|
||||||
|
opt::multi("", "check-cfg", "Provide list of valid cfg options for checking", "SPEC"),
|
||||||
opt::multi_s(
|
opt::multi_s(
|
||||||
"L",
|
"L",
|
||||||
"",
|
"",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//! Contains `ParseSess` which holds state living beyond what one `Parser` might.
|
//! Contains `ParseSess` which holds state living beyond what one `Parser` might.
|
||||||
//! It also serves as an input to the parser itself.
|
//! It also serves as an input to the parser itself.
|
||||||
|
|
||||||
|
use crate::config::CheckCfg;
|
||||||
use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId};
|
use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId};
|
||||||
use rustc_ast::node_id::NodeId;
|
use rustc_ast::node_id::NodeId;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
|
@ -18,6 +19,7 @@ use std::str;
|
||||||
/// The set of keys (and, optionally, values) that define the compilation
|
/// The set of keys (and, optionally, values) that define the compilation
|
||||||
/// environment of the crate, used to drive conditional compilation.
|
/// environment of the crate, used to drive conditional compilation.
|
||||||
pub type CrateConfig = FxHashSet<(Symbol, Option<Symbol>)>;
|
pub type CrateConfig = FxHashSet<(Symbol, Option<Symbol>)>;
|
||||||
|
pub type CrateCheckConfig = CheckCfg<Symbol>;
|
||||||
|
|
||||||
/// Collected spans during parsing for places where a certain feature was
|
/// Collected spans during parsing for places where a certain feature was
|
||||||
/// used and should be feature gated accordingly in `check_crate`.
|
/// used and should be feature gated accordingly in `check_crate`.
|
||||||
|
@ -117,6 +119,7 @@ pub struct ParseSess {
|
||||||
pub span_diagnostic: Handler,
|
pub span_diagnostic: Handler,
|
||||||
pub unstable_features: UnstableFeatures,
|
pub unstable_features: UnstableFeatures,
|
||||||
pub config: CrateConfig,
|
pub config: CrateConfig,
|
||||||
|
pub check_config: CrateCheckConfig,
|
||||||
pub edition: Edition,
|
pub edition: Edition,
|
||||||
pub missing_fragment_specifiers: Lock<FxHashMap<Span, NodeId>>,
|
pub missing_fragment_specifiers: Lock<FxHashMap<Span, NodeId>>,
|
||||||
/// Places where raw identifiers were used. This is used to avoid complaining about idents
|
/// Places where raw identifiers were used. This is used to avoid complaining about idents
|
||||||
|
@ -162,6 +165,7 @@ impl ParseSess {
|
||||||
span_diagnostic: handler,
|
span_diagnostic: handler,
|
||||||
unstable_features: UnstableFeatures::from_environment(None),
|
unstable_features: UnstableFeatures::from_environment(None),
|
||||||
config: FxHashSet::default(),
|
config: FxHashSet::default(),
|
||||||
|
check_config: CrateCheckConfig::default(),
|
||||||
edition: ExpnId::root().expn_data().edition,
|
edition: ExpnId::root().expn_data().edition,
|
||||||
missing_fragment_specifiers: Default::default(),
|
missing_fragment_specifiers: Default::default(),
|
||||||
raw_identifier_spans: Lock::new(Vec::new()),
|
raw_identifier_spans: Lock::new(Vec::new()),
|
||||||
|
|
|
@ -911,6 +911,7 @@ symbols! {
|
||||||
naked,
|
naked,
|
||||||
naked_functions,
|
naked_functions,
|
||||||
name,
|
name,
|
||||||
|
names,
|
||||||
native_link_modifiers,
|
native_link_modifiers,
|
||||||
native_link_modifiers_as_needed,
|
native_link_modifiers_as_needed,
|
||||||
native_link_modifiers_bundle,
|
native_link_modifiers_bundle,
|
||||||
|
@ -1482,6 +1483,7 @@ symbols! {
|
||||||
va_list,
|
va_list,
|
||||||
va_start,
|
va_start,
|
||||||
val,
|
val,
|
||||||
|
values,
|
||||||
var,
|
var,
|
||||||
variant_count,
|
variant_count,
|
||||||
vec,
|
vec,
|
||||||
|
|
|
@ -253,6 +253,7 @@ crate fn create_config(
|
||||||
interface::Config {
|
interface::Config {
|
||||||
opts: sessopts,
|
opts: sessopts,
|
||||||
crate_cfg: interface::parse_cfgspecs(cfgs),
|
crate_cfg: interface::parse_cfgspecs(cfgs),
|
||||||
|
crate_check_cfg: interface::parse_check_cfg(vec![]),
|
||||||
input,
|
input,
|
||||||
input_path: cpath,
|
input_path: cpath,
|
||||||
output_file: None,
|
output_file: None,
|
||||||
|
|
|
@ -91,6 +91,7 @@ crate fn run(options: RustdocOptions) -> Result<(), ErrorReported> {
|
||||||
let config = interface::Config {
|
let config = interface::Config {
|
||||||
opts: sessopts,
|
opts: sessopts,
|
||||||
crate_cfg: interface::parse_cfgspecs(cfgs),
|
crate_cfg: interface::parse_cfgspecs(cfgs),
|
||||||
|
crate_check_cfg: interface::parse_check_cfg(vec![]),
|
||||||
input,
|
input,
|
||||||
input_path: None,
|
input_path: None,
|
||||||
output_file: None,
|
output_file: None,
|
||||||
|
|
|
@ -49,6 +49,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf) {
|
||||||
let config = interface::Config {
|
let config = interface::Config {
|
||||||
opts,
|
opts,
|
||||||
crate_cfg: Default::default(),
|
crate_cfg: Default::default(),
|
||||||
|
crate_check_cfg: Default::default(),
|
||||||
input,
|
input,
|
||||||
input_path: None,
|
input_path: None,
|
||||||
output_file: Some(output),
|
output_file: Some(output),
|
||||||
|
|
10
src/test/ui/check-cfg/empty-names.rs
Normal file
10
src/test/ui/check-cfg/empty-names.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// Check warning for unexpected cfg
|
||||||
|
//
|
||||||
|
// check-pass
|
||||||
|
// compile-flags: --check-cfg=names() -Z unstable-options
|
||||||
|
|
||||||
|
#[cfg(unknown_key = "value")]
|
||||||
|
//~^ WARNING unexpected `cfg` condition name
|
||||||
|
pub fn f() {}
|
||||||
|
|
||||||
|
fn main() {}
|
10
src/test/ui/check-cfg/empty-names.stderr
Normal file
10
src/test/ui/check-cfg/empty-names.stderr
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
warning: unexpected `cfg` condition name
|
||||||
|
--> $DIR/empty-names.rs:6:7
|
||||||
|
|
|
||||||
|
LL | #[cfg(unknown_key = "value")]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
6
src/test/ui/check-cfg/empty-values.rs
Normal file
6
src/test/ui/check-cfg/empty-values.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// Check that a an empty values() is rejected
|
||||||
|
//
|
||||||
|
// check-fail
|
||||||
|
// compile-flags: --check-cfg=values() -Z unstable-options
|
||||||
|
|
||||||
|
fn main() {}
|
2
src/test/ui/check-cfg/empty-values.stderr
Normal file
2
src/test/ui/check-cfg/empty-values.stderr
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
error: invalid `--check-cfg` argument: `values()` (expected `names(name1, name2, ... nameN)` or `values(name, "value1", "value2", ... "valueN")`)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
error: invalid `--check-cfg` argument: `anything_else(...)` (expected `names(name1, name2, ... nameN)` or `values(name, "value1", "value2", ... "valueN")`)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
error: invalid `--check-cfg` argument: `names("NOT_IDENT")` (`names()` arguments must be simple identifers)
|
||||||
|
|
10
src/test/ui/check-cfg/invalid-arguments.rs
Normal file
10
src/test/ui/check-cfg/invalid-arguments.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// Check that invalid --check-cfg are rejected
|
||||||
|
//
|
||||||
|
// check-fail
|
||||||
|
// revisions: anything_else names_simple_ident values_simple_ident values_string_literals
|
||||||
|
// [anything_else]compile-flags: -Z unstable-options --check-cfg=anything_else(...)
|
||||||
|
// [names_simple_ident]compile-flags: -Z unstable-options --check-cfg=names("NOT_IDENT")
|
||||||
|
// [values_simple_ident]compile-flags: -Z unstable-options --check-cfg=values("NOT_IDENT")
|
||||||
|
// [values_string_literals]compile-flags: -Z unstable-options --check-cfg=values(test,12)
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,2 @@
|
||||||
|
error: invalid `--check-cfg` argument: `values("NOT_IDENT")` (`values()` first argument must be a simple identifer)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
error: invalid `--check-cfg` argument: `values(test,12)` (`values()` arguments must be string literals)
|
||||||
|
|
14
src/test/ui/check-cfg/invalid-cfg-name.rs
Normal file
14
src/test/ui/check-cfg/invalid-cfg-name.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Check warning for invalid configuration name
|
||||||
|
//
|
||||||
|
// edition:2018
|
||||||
|
// check-pass
|
||||||
|
// compile-flags: --check-cfg=names() -Z unstable-options
|
||||||
|
|
||||||
|
#[cfg(widnows)]
|
||||||
|
//~^ WARNING unexpected `cfg` condition name
|
||||||
|
pub fn f() {}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub fn g() {}
|
||||||
|
|
||||||
|
pub fn main() {}
|
10
src/test/ui/check-cfg/invalid-cfg-name.stderr
Normal file
10
src/test/ui/check-cfg/invalid-cfg-name.stderr
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
warning: unexpected `cfg` condition name
|
||||||
|
--> $DIR/invalid-cfg-name.rs:7:7
|
||||||
|
|
|
||||||
|
LL | #[cfg(widnows)]
|
||||||
|
| ^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
17
src/test/ui/check-cfg/invalid-cfg-value.rs
Normal file
17
src/test/ui/check-cfg/invalid-cfg-value.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Check warning for invalid configuration value
|
||||||
|
//
|
||||||
|
// edition:2018
|
||||||
|
// check-pass
|
||||||
|
// compile-flags: --check-cfg=values(feature,"serde","full") --cfg=feature="rand" -Z unstable-options
|
||||||
|
|
||||||
|
#[cfg(feature = "sedre")]
|
||||||
|
//~^ WARNING unexpected `cfg` condition value
|
||||||
|
pub fn f() {}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub fn g() {}
|
||||||
|
|
||||||
|
#[cfg(feature = "rand")]
|
||||||
|
pub fn h() {}
|
||||||
|
|
||||||
|
pub fn main() {}
|
10
src/test/ui/check-cfg/invalid-cfg-value.stderr
Normal file
10
src/test/ui/check-cfg/invalid-cfg-value.stderr
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
warning: unexpected `cfg` condition value
|
||||||
|
--> $DIR/invalid-cfg-value.rs:7:7
|
||||||
|
|
|
||||||
|
LL | #[cfg(feature = "sedre")]
|
||||||
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
3
src/test/ui/feature-gates/feature-gate-check-cfg.rs
Normal file
3
src/test/ui/feature-gates/feature-gate-check-cfg.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// compile-flags: --check-cfg "names()"
|
||||||
|
|
||||||
|
fn main() {}
|
2
src/test/ui/feature-gates/feature-gate-check-cfg.stderr
Normal file
2
src/test/ui/feature-gates/feature-gate-check-cfg.stderr
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
error: the `-Z unstable-options` flag must also be passed to enable the flag `check-cfg`
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::path::Path;
|
||||||
|
|
||||||
const ENTRY_LIMIT: usize = 1000;
|
const ENTRY_LIMIT: usize = 1000;
|
||||||
// FIXME: The following limits should be reduced eventually.
|
// FIXME: The following limits should be reduced eventually.
|
||||||
const ROOT_ENTRY_LIMIT: usize = 982;
|
const ROOT_ENTRY_LIMIT: usize = 983;
|
||||||
const ISSUES_ENTRY_LIMIT: usize = 2310;
|
const ISSUES_ENTRY_LIMIT: usize = 2310;
|
||||||
|
|
||||||
fn check_entries(path: &Path, bad: &mut bool) {
|
fn check_entries(path: &Path, bad: &mut bool) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue