about summary refs log tree commit diff
path: root/compiler/rustc_interface
diff options
context:
space:
mode:
authorLoïc BRANSTETT <lolo.branstett@numericable.fr>2021-09-29 02:39:30 +0200
committerLoïc BRANSTETT <lolo.branstett@numericable.fr>2022-02-16 13:03:12 +0100
commit3a73ca587bb8a8fb52d6045fbe31d50d5a56ff19 (patch)
tree37a0d4e25436b3524bb8e1b876f009dad1824f64 /compiler/rustc_interface
parent6499c5e7fc173a3f55b7a3bd1e6a50e9edef782d (diff)
downloadrust-3a73ca587bb8a8fb52d6045fbe31d50d5a56ff19.tar.gz
rust-3a73ca587bb8a8fb52d6045fbe31d50d5a56ff19.zip
Implement --check-cfg option (RFC 3013)
Co-authored-by: Urgau <lolo.branstett@numericable.fr>
Co-authored-by: Marcelina Kościelnicka <mwk@0x04.net>
Diffstat (limited to 'compiler/rustc_interface')
-rw-r--r--compiler/rustc_interface/src/interface.rs91
-rw-r--r--compiler/rustc_interface/src/util.rs8
2 files changed, 97 insertions, 2 deletions
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 237aef1cf23..81d33411c4e 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -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;
 use std::sync::{Arc, Mutex};
@@ -140,6 +141,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
@@ -147,6 +232,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>,
@@ -190,6 +276,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(),
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 6d9183eda9d..4b1b01de549 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -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};
@@ -67,6 +68,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>,
@@ -102,7 +104,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))
 }