Delay parsing of --cfg
and --check-cfg
options.
By storing the unparsed values in `Config` and then parsing them within `run_compiler`, the parsing functions can use the main symbol interner, and not create their own short-lived interners. This change also eliminates the need for one `EarlyErrorHandler` in rustdoc, because parsing errors can be reported by another, slightly later `EarlyErrorHandler`.
This commit is contained in:
parent
ec2b311914
commit
678e01a3fc
5 changed files with 271 additions and 289 deletions
|
@ -317,13 +317,11 @@ fn run_compiler(
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let cfg = interface::parse_cfg(&early_error_handler, matches.opt_strs("cfg"));
|
|
||||||
let check_cfg = interface::parse_check_cfg(&early_error_handler, 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: matches.opt_strs("cfg"),
|
||||||
crate_check_cfg: check_cfg,
|
crate_check_cfg: matches.opt_strs("check-cfg"),
|
||||||
input: Input::File(PathBuf::new()),
|
input: Input::File(PathBuf::new()),
|
||||||
output_file: ofile,
|
output_file: ofile,
|
||||||
output_dir: odir,
|
output_dir: odir,
|
||||||
|
|
|
@ -65,295 +65,286 @@ impl Compiler {
|
||||||
|
|
||||||
/// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`.
|
/// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`.
|
||||||
pub fn parse_cfg(handler: &EarlyErrorHandler, cfgs: Vec<String>) -> Cfg<String> {
|
pub fn parse_cfg(handler: &EarlyErrorHandler, cfgs: Vec<String>) -> Cfg<String> {
|
||||||
// This creates a short-lived `SessionGlobals`, containing an interner. The
|
cfgs.into_iter()
|
||||||
// parsed values are converted from symbols to strings before exiting
|
.map(|s| {
|
||||||
// because the symbols are meaningless once the interner is gone.
|
|
||||||
rustc_span::create_default_session_if_not_set_then(move |_| {
|
|
||||||
cfgs.into_iter()
|
|
||||||
.map(|s| {
|
|
||||||
let sess = ParseSess::with_silent_emitter(Some(format!(
|
|
||||||
"this error occurred on the command line: `--cfg={s}`"
|
|
||||||
)));
|
|
||||||
let filename = FileName::cfg_spec_source_code(&s);
|
|
||||||
|
|
||||||
macro_rules! error {
|
|
||||||
($reason: expr) => {
|
|
||||||
handler.early_error(format!(
|
|
||||||
concat!("invalid `--cfg` argument: `{}` (", $reason, ")"),
|
|
||||||
s
|
|
||||||
));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
|
|
||||||
Ok(mut parser) => match parser.parse_meta_item() {
|
|
||||||
Ok(meta_item) if parser.token == token::Eof => {
|
|
||||||
if meta_item.path.segments.len() != 1 {
|
|
||||||
error!("argument key must be an identifier");
|
|
||||||
}
|
|
||||||
match &meta_item.kind {
|
|
||||||
MetaItemKind::List(..) => {}
|
|
||||||
MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
|
|
||||||
error!("argument value must be a string");
|
|
||||||
}
|
|
||||||
MetaItemKind::NameValue(..) | MetaItemKind::Word => {
|
|
||||||
let ident = meta_item.ident().expect("multi-segment cfg key");
|
|
||||||
return (
|
|
||||||
ident.name.to_string(),
|
|
||||||
meta_item.value_str().map(|sym| sym.to_string()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(..) => {}
|
|
||||||
Err(err) => err.cancel(),
|
|
||||||
},
|
|
||||||
Err(errs) => drop(errs),
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user tried to use a key="value" flag, but is missing the quotes, provide
|
|
||||||
// a hint about how to resolve this.
|
|
||||||
if s.contains('=') && !s.contains("=\"") && !s.ends_with('"') {
|
|
||||||
error!(concat!(
|
|
||||||
r#"expected `key` or `key="value"`, ensure escaping is appropriate"#,
|
|
||||||
r#" for your shell, try 'key="value"' or key=\"value\""#
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
error!(r#"expected `key` or `key="value"`"#);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Cfg<String>>()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
|
|
||||||
pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> CheckCfg<String> {
|
|
||||||
// The comment about `SessionGlobals` and symbols in `parse_cfg` above
|
|
||||||
// applies here too.
|
|
||||||
rustc_span::create_default_session_if_not_set_then(move |_| {
|
|
||||||
// If any --check-cfg is passed then exhaustive_values and exhaustive_names
|
|
||||||
// are enabled by default.
|
|
||||||
let exhaustive_names = !specs.is_empty();
|
|
||||||
let exhaustive_values = !specs.is_empty();
|
|
||||||
let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() };
|
|
||||||
|
|
||||||
let mut old_syntax = None;
|
|
||||||
for s in specs {
|
|
||||||
let sess = ParseSess::with_silent_emitter(Some(format!(
|
let sess = ParseSess::with_silent_emitter(Some(format!(
|
||||||
"this error occurred on the command line: `--check-cfg={s}`"
|
"this error occurred on the command line: `--cfg={s}`"
|
||||||
)));
|
)));
|
||||||
let filename = FileName::cfg_spec_source_code(&s);
|
let filename = FileName::cfg_spec_source_code(&s);
|
||||||
|
|
||||||
macro_rules! error {
|
macro_rules! error {
|
||||||
($reason:expr) => {
|
($reason: expr) => {
|
||||||
handler.early_error(format!(
|
handler.early_error(format!(
|
||||||
concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
|
concat!("invalid `--cfg` argument: `{}` (", $reason, ")"),
|
||||||
s
|
s
|
||||||
))
|
));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected_error = || -> ! {
|
match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
|
||||||
error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`")
|
Ok(mut parser) => match parser.parse_meta_item() {
|
||||||
};
|
Ok(meta_item) if parser.token == token::Eof => {
|
||||||
|
if meta_item.path.segments.len() != 1 {
|
||||||
let Ok(mut parser) = maybe_new_parser_from_source_str(&sess, filename, s.to_string())
|
error!("argument key must be an identifier");
|
||||||
else {
|
}
|
||||||
expected_error();
|
match &meta_item.kind {
|
||||||
};
|
MetaItemKind::List(..) => {}
|
||||||
|
MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
|
||||||
let meta_item = match parser.parse_meta_item() {
|
error!("argument value must be a string");
|
||||||
Ok(meta_item) if parser.token == token::Eof => meta_item,
|
|
||||||
Ok(..) => expected_error(),
|
|
||||||
Err(err) => {
|
|
||||||
err.cancel();
|
|
||||||
expected_error();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(args) = meta_item.meta_item_list() else {
|
|
||||||
expected_error();
|
|
||||||
};
|
|
||||||
|
|
||||||
if meta_item.has_name(sym::names) {
|
|
||||||
// defaults are flipped for the old syntax
|
|
||||||
if old_syntax == None {
|
|
||||||
check_cfg.exhaustive_names = false;
|
|
||||||
check_cfg.exhaustive_values = false;
|
|
||||||
}
|
|
||||||
old_syntax = Some(true);
|
|
||||||
|
|
||||||
check_cfg.exhaustive_names = true;
|
|
||||||
for arg in args {
|
|
||||||
if arg.is_word() && arg.ident().is_some() {
|
|
||||||
let ident = arg.ident().expect("multi-segment cfg key");
|
|
||||||
check_cfg
|
|
||||||
.expecteds
|
|
||||||
.entry(ident.name.to_string())
|
|
||||||
.or_insert(ExpectedValues::Any);
|
|
||||||
} else {
|
|
||||||
error!("`names()` arguments must be simple identifiers");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if meta_item.has_name(sym::values) {
|
|
||||||
// defaults are flipped for the old syntax
|
|
||||||
if old_syntax == None {
|
|
||||||
check_cfg.exhaustive_names = false;
|
|
||||||
check_cfg.exhaustive_values = false;
|
|
||||||
}
|
|
||||||
old_syntax = Some(true);
|
|
||||||
|
|
||||||
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");
|
|
||||||
let expected_values = check_cfg
|
|
||||||
.expecteds
|
|
||||||
.entry(ident.name.to_string())
|
|
||||||
.and_modify(|expected_values| match expected_values {
|
|
||||||
ExpectedValues::Some(_) => {}
|
|
||||||
ExpectedValues::Any => {
|
|
||||||
// handle the case where names(...) was done
|
|
||||||
// before values by changing to a list
|
|
||||||
*expected_values = ExpectedValues::Some(FxHashSet::default());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.or_insert_with(|| ExpectedValues::Some(FxHashSet::default()));
|
|
||||||
|
|
||||||
let ExpectedValues::Some(expected_values) = expected_values else {
|
|
||||||
bug!("`expected_values` should be a list a values")
|
|
||||||
};
|
|
||||||
|
|
||||||
for val in values {
|
|
||||||
if let Some(LitKind::Str(s, _)) = val.lit().map(|lit| &lit.kind) {
|
|
||||||
expected_values.insert(Some(s.to_string()));
|
|
||||||
} else {
|
|
||||||
error!("`values()` arguments must be string literals");
|
|
||||||
}
|
}
|
||||||
}
|
MetaItemKind::NameValue(..) | MetaItemKind::Word => {
|
||||||
|
let ident = meta_item.ident().expect("multi-segment cfg key");
|
||||||
if values.is_empty() {
|
return (
|
||||||
expected_values.insert(None);
|
ident.name.to_string(),
|
||||||
}
|
meta_item.value_str().map(|sym| sym.to_string()),
|
||||||
} else {
|
|
||||||
error!("`values()` first argument must be a simple identifier");
|
|
||||||
}
|
|
||||||
} else if args.is_empty() {
|
|
||||||
check_cfg.exhaustive_values = true;
|
|
||||||
} else {
|
|
||||||
expected_error();
|
|
||||||
}
|
|
||||||
} else if meta_item.has_name(sym::cfg) {
|
|
||||||
old_syntax = Some(false);
|
|
||||||
|
|
||||||
let mut names = Vec::new();
|
|
||||||
let mut values: FxHashSet<_> = Default::default();
|
|
||||||
|
|
||||||
let mut any_specified = false;
|
|
||||||
let mut values_specified = false;
|
|
||||||
let mut values_any_specified = false;
|
|
||||||
|
|
||||||
for arg in args {
|
|
||||||
if arg.is_word() && let Some(ident) = arg.ident() {
|
|
||||||
if values_specified {
|
|
||||||
error!("`cfg()` names cannot be after values");
|
|
||||||
}
|
|
||||||
names.push(ident);
|
|
||||||
} else if arg.has_name(sym::any)
|
|
||||||
&& let Some(args) = arg.meta_item_list()
|
|
||||||
{
|
|
||||||
if any_specified {
|
|
||||||
error!("`any()` cannot be specified multiple times");
|
|
||||||
}
|
|
||||||
any_specified = true;
|
|
||||||
if !args.is_empty() {
|
|
||||||
error!("`any()` must be empty");
|
|
||||||
}
|
|
||||||
} else if arg.has_name(sym::values)
|
|
||||||
&& let Some(args) = arg.meta_item_list()
|
|
||||||
{
|
|
||||||
if names.is_empty() {
|
|
||||||
error!("`values()` cannot be specified before the names");
|
|
||||||
} else if values_specified {
|
|
||||||
error!("`values()` cannot be specified multiple times");
|
|
||||||
}
|
|
||||||
values_specified = true;
|
|
||||||
|
|
||||||
for arg in args {
|
|
||||||
if let Some(LitKind::Str(s, _)) =
|
|
||||||
arg.lit().map(|lit| &lit.kind)
|
|
||||||
{
|
|
||||||
values.insert(Some(s.to_string()));
|
|
||||||
} else if arg.has_name(sym::any)
|
|
||||||
&& let Some(args) = arg.meta_item_list()
|
|
||||||
{
|
|
||||||
if values_any_specified {
|
|
||||||
error!(
|
|
||||||
"`any()` in `values()` cannot be specified multiple times"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
values_any_specified = true;
|
|
||||||
if !args.is_empty() {
|
|
||||||
error!("`any()` must be empty");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error!(
|
|
||||||
"`values()` arguments must be string literals or `any()`"
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
error!(
|
|
||||||
"`cfg()` arguments must be simple identifiers, `any()` or `values(...)`"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
Ok(..) => {}
|
||||||
|
Err(err) => err.cancel(),
|
||||||
|
},
|
||||||
|
Err(errs) => drop(errs),
|
||||||
|
}
|
||||||
|
|
||||||
if values.is_empty() && !values_any_specified && !any_specified {
|
// If the user tried to use a key="value" flag, but is missing the quotes, provide
|
||||||
values.insert(None);
|
// a hint about how to resolve this.
|
||||||
} else if !values.is_empty() && values_any_specified {
|
if s.contains('=') && !s.contains("=\"") && !s.ends_with('"') {
|
||||||
error!(
|
error!(concat!(
|
||||||
"`values()` arguments cannot specify string literals and `any()` at the same time"
|
r#"expected `key` or `key="value"`, ensure escaping is appropriate"#,
|
||||||
);
|
r#" for your shell, try 'key="value"' or key=\"value\""#
|
||||||
}
|
));
|
||||||
|
} else {
|
||||||
|
error!(r#"expected `key` or `key="value"`"#);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Cfg<String>>()
|
||||||
|
}
|
||||||
|
|
||||||
if any_specified {
|
/// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
|
||||||
if names.is_empty()
|
pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> CheckCfg<String> {
|
||||||
&& values.is_empty()
|
// If any --check-cfg is passed then exhaustive_values and exhaustive_names
|
||||||
&& !values_specified
|
// are enabled by default.
|
||||||
&& !values_any_specified
|
let exhaustive_names = !specs.is_empty();
|
||||||
{
|
let exhaustive_values = !specs.is_empty();
|
||||||
check_cfg.exhaustive_names = false;
|
let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() };
|
||||||
} else {
|
|
||||||
error!("`cfg(any())` can only be provided in isolation");
|
let mut old_syntax = None;
|
||||||
|
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) => {
|
||||||
|
handler.early_error(format!(
|
||||||
|
concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
|
||||||
|
s
|
||||||
|
))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected_error = || -> ! {
|
||||||
|
error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`")
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(mut parser) = maybe_new_parser_from_source_str(&sess, filename, s.to_string())
|
||||||
|
else {
|
||||||
|
expected_error();
|
||||||
|
};
|
||||||
|
|
||||||
|
let meta_item = match parser.parse_meta_item() {
|
||||||
|
Ok(meta_item) if parser.token == token::Eof => meta_item,
|
||||||
|
Ok(..) => expected_error(),
|
||||||
|
Err(err) => {
|
||||||
|
err.cancel();
|
||||||
|
expected_error();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(args) = meta_item.meta_item_list() else {
|
||||||
|
expected_error();
|
||||||
|
};
|
||||||
|
|
||||||
|
if meta_item.has_name(sym::names) {
|
||||||
|
// defaults are flipped for the old syntax
|
||||||
|
if old_syntax == None {
|
||||||
|
check_cfg.exhaustive_names = false;
|
||||||
|
check_cfg.exhaustive_values = false;
|
||||||
|
}
|
||||||
|
old_syntax = Some(true);
|
||||||
|
|
||||||
|
check_cfg.exhaustive_names = true;
|
||||||
|
for arg in args {
|
||||||
|
if arg.is_word() && arg.ident().is_some() {
|
||||||
|
let ident = arg.ident().expect("multi-segment cfg key");
|
||||||
|
check_cfg
|
||||||
|
.expecteds
|
||||||
|
.entry(ident.name.to_string())
|
||||||
|
.or_insert(ExpectedValues::Any);
|
||||||
|
} else {
|
||||||
|
error!("`names()` arguments must be simple identifiers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if meta_item.has_name(sym::values) {
|
||||||
|
// defaults are flipped for the old syntax
|
||||||
|
if old_syntax == None {
|
||||||
|
check_cfg.exhaustive_names = false;
|
||||||
|
check_cfg.exhaustive_values = false;
|
||||||
|
}
|
||||||
|
old_syntax = Some(true);
|
||||||
|
|
||||||
|
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");
|
||||||
|
let expected_values = check_cfg
|
||||||
|
.expecteds
|
||||||
|
.entry(ident.name.to_string())
|
||||||
|
.and_modify(|expected_values| match expected_values {
|
||||||
|
ExpectedValues::Some(_) => {}
|
||||||
|
ExpectedValues::Any => {
|
||||||
|
// handle the case where names(...) was done
|
||||||
|
// before values by changing to a list
|
||||||
|
*expected_values = ExpectedValues::Some(FxHashSet::default());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or_insert_with(|| ExpectedValues::Some(FxHashSet::default()));
|
||||||
|
|
||||||
|
let ExpectedValues::Some(expected_values) = expected_values else {
|
||||||
|
bug!("`expected_values` should be a list a values")
|
||||||
|
};
|
||||||
|
|
||||||
|
for val in values {
|
||||||
|
if let Some(LitKind::Str(s, _)) = val.lit().map(|lit| &lit.kind) {
|
||||||
|
expected_values.insert(Some(s.to_string()));
|
||||||
|
} else {
|
||||||
|
error!("`values()` arguments must be string literals");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if values.is_empty() {
|
||||||
|
expected_values.insert(None);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for name in names {
|
error!("`values()` first argument must be a simple identifier");
|
||||||
check_cfg
|
|
||||||
.expecteds
|
|
||||||
.entry(name.to_string())
|
|
||||||
.and_modify(|v| match v {
|
|
||||||
ExpectedValues::Some(v) if !values_any_specified => {
|
|
||||||
v.extend(values.clone())
|
|
||||||
}
|
|
||||||
ExpectedValues::Some(_) => *v = ExpectedValues::Any,
|
|
||||||
ExpectedValues::Any => {}
|
|
||||||
})
|
|
||||||
.or_insert_with(|| {
|
|
||||||
if values_any_specified {
|
|
||||||
ExpectedValues::Any
|
|
||||||
} else {
|
|
||||||
ExpectedValues::Some(values.clone())
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else if args.is_empty() {
|
||||||
|
check_cfg.exhaustive_values = true;
|
||||||
} else {
|
} else {
|
||||||
expected_error();
|
expected_error();
|
||||||
}
|
}
|
||||||
}
|
} else if meta_item.has_name(sym::cfg) {
|
||||||
|
old_syntax = Some(false);
|
||||||
|
|
||||||
check_cfg
|
let mut names = Vec::new();
|
||||||
})
|
let mut values: FxHashSet<_> = Default::default();
|
||||||
|
|
||||||
|
let mut any_specified = false;
|
||||||
|
let mut values_specified = false;
|
||||||
|
let mut values_any_specified = false;
|
||||||
|
|
||||||
|
for arg in args {
|
||||||
|
if arg.is_word() && let Some(ident) = arg.ident() {
|
||||||
|
if values_specified {
|
||||||
|
error!("`cfg()` names cannot be after values");
|
||||||
|
}
|
||||||
|
names.push(ident);
|
||||||
|
} else if arg.has_name(sym::any)
|
||||||
|
&& let Some(args) = arg.meta_item_list()
|
||||||
|
{
|
||||||
|
if any_specified {
|
||||||
|
error!("`any()` cannot be specified multiple times");
|
||||||
|
}
|
||||||
|
any_specified = true;
|
||||||
|
if !args.is_empty() {
|
||||||
|
error!("`any()` must be empty");
|
||||||
|
}
|
||||||
|
} else if arg.has_name(sym::values)
|
||||||
|
&& let Some(args) = arg.meta_item_list()
|
||||||
|
{
|
||||||
|
if names.is_empty() {
|
||||||
|
error!("`values()` cannot be specified before the names");
|
||||||
|
} else if values_specified {
|
||||||
|
error!("`values()` cannot be specified multiple times");
|
||||||
|
}
|
||||||
|
values_specified = true;
|
||||||
|
|
||||||
|
for arg in args {
|
||||||
|
if let Some(LitKind::Str(s, _)) =
|
||||||
|
arg.lit().map(|lit| &lit.kind)
|
||||||
|
{
|
||||||
|
values.insert(Some(s.to_string()));
|
||||||
|
} else if arg.has_name(sym::any)
|
||||||
|
&& let Some(args) = arg.meta_item_list()
|
||||||
|
{
|
||||||
|
if values_any_specified {
|
||||||
|
error!(
|
||||||
|
"`any()` in `values()` cannot be specified multiple times"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
values_any_specified = true;
|
||||||
|
if !args.is_empty() {
|
||||||
|
error!("`any()` must be empty");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"`values()` arguments must be string literals or `any()`"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"`cfg()` arguments must be simple identifiers, `any()` or `values(...)`"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if values.is_empty() && !values_any_specified && !any_specified {
|
||||||
|
values.insert(None);
|
||||||
|
} else if !values.is_empty() && values_any_specified {
|
||||||
|
error!(
|
||||||
|
"`values()` arguments cannot specify string literals and `any()` at the same time"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if any_specified {
|
||||||
|
if names.is_empty()
|
||||||
|
&& values.is_empty()
|
||||||
|
&& !values_specified
|
||||||
|
&& !values_any_specified
|
||||||
|
{
|
||||||
|
check_cfg.exhaustive_names = false;
|
||||||
|
} else {
|
||||||
|
error!("`cfg(any())` can only be provided in isolation");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for name in names {
|
||||||
|
check_cfg
|
||||||
|
.expecteds
|
||||||
|
.entry(name.to_string())
|
||||||
|
.and_modify(|v| match v {
|
||||||
|
ExpectedValues::Some(v) if !values_any_specified => {
|
||||||
|
v.extend(values.clone())
|
||||||
|
}
|
||||||
|
ExpectedValues::Some(_) => *v = ExpectedValues::Any,
|
||||||
|
ExpectedValues::Any => {}
|
||||||
|
})
|
||||||
|
.or_insert_with(|| {
|
||||||
|
if values_any_specified {
|
||||||
|
ExpectedValues::Any
|
||||||
|
} else {
|
||||||
|
ExpectedValues::Some(values.clone())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expected_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check_cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The compiler configuration
|
/// The compiler configuration
|
||||||
|
@ -361,9 +352,9 @@ pub struct Config {
|
||||||
/// Command line options
|
/// Command line options
|
||||||
pub opts: config::Options,
|
pub opts: config::Options,
|
||||||
|
|
||||||
/// cfg! configuration in addition to the default ones
|
/// Unparsed cfg! configuration in addition to the default ones.
|
||||||
pub crate_cfg: Cfg<String>,
|
pub crate_cfg: Vec<String>,
|
||||||
pub crate_check_cfg: CheckCfg<String>,
|
pub crate_check_cfg: Vec<String>,
|
||||||
|
|
||||||
pub input: Input,
|
pub input: Input,
|
||||||
pub output_dir: Option<PathBuf>,
|
pub output_dir: Option<PathBuf>,
|
||||||
|
@ -436,8 +427,8 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
|
||||||
let (mut sess, codegen_backend) = util::create_session(
|
let (mut sess, codegen_backend) = util::create_session(
|
||||||
&handler,
|
&handler,
|
||||||
config.opts,
|
config.opts,
|
||||||
config.crate_cfg,
|
parse_cfg(&handler, config.crate_cfg),
|
||||||
config.crate_check_cfg,
|
parse_check_cfg(&handler, config.crate_check_cfg),
|
||||||
config.locale_resources,
|
config.locale_resources,
|
||||||
config.file_loader,
|
config.file_loader,
|
||||||
CompilerIO {
|
CompilerIO {
|
||||||
|
|
|
@ -14,8 +14,8 @@ use rustc_lint::{late_lint_mod, MissingDoc};
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
||||||
use rustc_session::config::{self, CrateType, ErrorOutputType, ResolveDocLinks};
|
use rustc_session::config::{self, CrateType, ErrorOutputType, ResolveDocLinks};
|
||||||
|
use rustc_session::lint;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_session::{lint, EarlyErrorHandler};
|
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::{source_map, Span};
|
use rustc_span::{source_map, Span};
|
||||||
|
|
||||||
|
@ -175,7 +175,6 @@ pub(crate) fn new_handler(
|
||||||
|
|
||||||
/// Parse, resolve, and typecheck the given crate.
|
/// Parse, resolve, and typecheck the given crate.
|
||||||
pub(crate) fn create_config(
|
pub(crate) fn create_config(
|
||||||
handler: &EarlyErrorHandler,
|
|
||||||
RustdocOptions {
|
RustdocOptions {
|
||||||
input,
|
input,
|
||||||
crate_name,
|
crate_name,
|
||||||
|
@ -255,8 +254,8 @@ pub(crate) fn create_config(
|
||||||
|
|
||||||
interface::Config {
|
interface::Config {
|
||||||
opts: sessopts,
|
opts: sessopts,
|
||||||
crate_cfg: interface::parse_cfg(handler, cfgs),
|
crate_cfg: cfgs,
|
||||||
crate_check_cfg: interface::parse_check_cfg(handler, check_cfgs),
|
crate_check_cfg: check_cfgs,
|
||||||
input,
|
input,
|
||||||
output_file: None,
|
output_file: None,
|
||||||
output_dir: None,
|
output_dir: None,
|
||||||
|
|
|
@ -13,7 +13,7 @@ use rustc_parse::parser::attr::InnerAttrPolicy;
|
||||||
use rustc_resolve::rustdoc::span_of_fragments;
|
use rustc_resolve::rustdoc::span_of_fragments;
|
||||||
use rustc_session::config::{self, CrateType, ErrorOutputType};
|
use rustc_session::config::{self, CrateType, ErrorOutputType};
|
||||||
use rustc_session::parse::ParseSess;
|
use rustc_session::parse::ParseSess;
|
||||||
use rustc_session::{lint, EarlyErrorHandler, Session};
|
use rustc_session::{lint, Session};
|
||||||
use rustc_span::edition::Edition;
|
use rustc_span::edition::Edition;
|
||||||
use rustc_span::source_map::SourceMap;
|
use rustc_span::source_map::SourceMap;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
@ -85,18 +85,13 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
|
||||||
..config::Options::default()
|
..config::Options::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let early_error_handler = EarlyErrorHandler::new(ErrorOutputType::default());
|
|
||||||
|
|
||||||
let mut cfgs = options.cfgs.clone();
|
let mut cfgs = options.cfgs.clone();
|
||||||
cfgs.push("doc".to_owned());
|
cfgs.push("doc".to_owned());
|
||||||
cfgs.push("doctest".to_owned());
|
cfgs.push("doctest".to_owned());
|
||||||
let config = interface::Config {
|
let config = interface::Config {
|
||||||
opts: sessopts,
|
opts: sessopts,
|
||||||
crate_cfg: interface::parse_cfg(&early_error_handler, cfgs),
|
crate_cfg: cfgs,
|
||||||
crate_check_cfg: interface::parse_check_cfg(
|
crate_check_cfg: options.check_cfgs.clone(),
|
||||||
&early_error_handler,
|
|
||||||
options.check_cfgs.clone(),
|
|
||||||
),
|
|
||||||
input,
|
input,
|
||||||
output_file: None,
|
output_file: None,
|
||||||
output_dir: None,
|
output_dir: None,
|
||||||
|
|
|
@ -757,8 +757,7 @@ fn main_args(
|
||||||
(false, true) => {
|
(false, true) => {
|
||||||
let input = options.input.clone();
|
let input = options.input.clone();
|
||||||
let edition = options.edition;
|
let edition = options.edition;
|
||||||
let config =
|
let config = core::create_config(options, &render_options, using_internal_features);
|
||||||
core::create_config(handler, options, &render_options, using_internal_features);
|
|
||||||
|
|
||||||
// `markdown::render` can invoke `doctest::make_test`, which
|
// `markdown::render` can invoke `doctest::make_test`, which
|
||||||
// requires session globals and a thread pool, so we use
|
// requires session globals and a thread pool, so we use
|
||||||
|
@ -791,7 +790,7 @@ fn main_args(
|
||||||
let scrape_examples_options = options.scrape_examples_options.clone();
|
let scrape_examples_options = options.scrape_examples_options.clone();
|
||||||
let bin_crate = options.bin_crate;
|
let bin_crate = options.bin_crate;
|
||||||
|
|
||||||
let config = core::create_config(handler, options, &render_options, using_internal_features);
|
let config = core::create_config(options, &render_options, using_internal_features);
|
||||||
|
|
||||||
interface::run_compiler(config, |compiler| {
|
interface::run_compiler(config, |compiler| {
|
||||||
let sess = compiler.session();
|
let sess = compiler.session();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue