Fix ice for feature-gated cfg attributes applied to the crate
Signed-off-by: Jonathan Brouwer <jonathantbrouwer@gmail.com>
This commit is contained in:
@@ -9,7 +9,7 @@ use rustc_session::parse::feature_err;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::context::{AcceptContext, ShouldEmit, Stage};
|
||||
use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser};
|
||||
use crate::{
|
||||
CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg,
|
||||
@@ -90,7 +90,7 @@ fn parse_cfg_entry_version<S: Stage>(
|
||||
list: &MetaItemListParser<'_>,
|
||||
meta_span: Span,
|
||||
) -> Option<CfgEntry> {
|
||||
try_gate_cfg(sym::version, meta_span, cx.sess(), Some(cx.features()));
|
||||
try_gate_cfg(sym::version, meta_span, cx.sess(), cx.features_option());
|
||||
let Some(version) = list.single() else {
|
||||
cx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: list.span });
|
||||
return None;
|
||||
@@ -119,7 +119,9 @@ fn parse_cfg_entry_target<S: Stage>(
|
||||
list: &MetaItemListParser<'_>,
|
||||
meta_span: Span,
|
||||
) -> Option<CfgEntry> {
|
||||
if !cx.features().cfg_target_compact() {
|
||||
if let Some(features) = cx.features_option()
|
||||
&& !features.cfg_target_compact()
|
||||
{
|
||||
feature_err(
|
||||
cx.sess(),
|
||||
sym::cfg_target_compact,
|
||||
@@ -186,12 +188,13 @@ pub fn eval_config_entry(
|
||||
cfg_entry: &CfgEntry,
|
||||
id: NodeId,
|
||||
features: Option<&Features>,
|
||||
emit_lints: ShouldEmit,
|
||||
) -> EvalConfigResult {
|
||||
match cfg_entry {
|
||||
CfgEntry::All(subs, ..) => {
|
||||
let mut all = None;
|
||||
for sub in subs {
|
||||
let res = eval_config_entry(sess, sub, id, features);
|
||||
let res = eval_config_entry(sess, sub, id, features, emit_lints);
|
||||
// We cannot short-circuit because `eval_config_entry` emits some lints
|
||||
if !res.as_bool() {
|
||||
all.get_or_insert(res);
|
||||
@@ -202,7 +205,7 @@ pub fn eval_config_entry(
|
||||
CfgEntry::Any(subs, span) => {
|
||||
let mut any = None;
|
||||
for sub in subs {
|
||||
let res = eval_config_entry(sess, sub, id, features);
|
||||
let res = eval_config_entry(sess, sub, id, features, emit_lints);
|
||||
// We cannot short-circuit because `eval_config_entry` emits some lints
|
||||
if res.as_bool() {
|
||||
any.get_or_insert(res);
|
||||
@@ -214,7 +217,7 @@ pub fn eval_config_entry(
|
||||
})
|
||||
}
|
||||
CfgEntry::Not(sub, span) => {
|
||||
if eval_config_entry(sess, sub, id, features).as_bool() {
|
||||
if eval_config_entry(sess, sub, id, features, emit_lints).as_bool() {
|
||||
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
|
||||
} else {
|
||||
EvalConfigResult::True
|
||||
@@ -228,24 +231,28 @@ pub fn eval_config_entry(
|
||||
}
|
||||
}
|
||||
CfgEntry::NameValue { name, name_span, value, span } => {
|
||||
match sess.psess.check_config.expecteds.get(name) {
|
||||
Some(ExpectedValues::Some(values)) if !values.contains(&value.map(|(v, _)| v)) => {
|
||||
id.emit_span_lint(
|
||||
sess,
|
||||
UNEXPECTED_CFGS,
|
||||
*span,
|
||||
BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value),
|
||||
);
|
||||
if let ShouldEmit::ErrorsAndLints = emit_lints {
|
||||
match sess.psess.check_config.expecteds.get(name) {
|
||||
Some(ExpectedValues::Some(values))
|
||||
if !values.contains(&value.map(|(v, _)| v)) =>
|
||||
{
|
||||
id.emit_span_lint(
|
||||
sess,
|
||||
UNEXPECTED_CFGS,
|
||||
*span,
|
||||
BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value),
|
||||
);
|
||||
}
|
||||
None if sess.psess.check_config.exhaustive_names => {
|
||||
id.emit_span_lint(
|
||||
sess,
|
||||
UNEXPECTED_CFGS,
|
||||
*span,
|
||||
BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value),
|
||||
);
|
||||
}
|
||||
_ => { /* not unexpected */ }
|
||||
}
|
||||
None if sess.psess.check_config.exhaustive_names => {
|
||||
id.emit_span_lint(
|
||||
sess,
|
||||
UNEXPECTED_CFGS,
|
||||
*span,
|
||||
BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value),
|
||||
);
|
||||
}
|
||||
_ => { /* not unexpected */ }
|
||||
}
|
||||
|
||||
if sess.psess.config.contains(&(*name, value.map(|(v, _)| v))) {
|
||||
|
||||
@@ -223,7 +223,7 @@ impl Stage for Early {
|
||||
sess: &'sess Session,
|
||||
diag: impl for<'x> Diagnostic<'x>,
|
||||
) -> ErrorGuaranteed {
|
||||
if self.emit_errors {
|
||||
if self.emit_errors.should_emit() {
|
||||
sess.dcx().emit_err(diag)
|
||||
} else {
|
||||
sess.dcx().create_err(diag).delay_as_bug()
|
||||
@@ -254,7 +254,7 @@ pub struct Early {
|
||||
/// Whether to emit errors or delay them as a bug
|
||||
/// For most attributes, the attribute will be parsed again in the `Late` stage and in this case the errors should be delayed
|
||||
/// But for some, such as `cfg`, the attribute will be removed before the `Late` stage so errors must be emitted
|
||||
pub emit_errors: bool,
|
||||
pub emit_errors: ShouldEmit,
|
||||
}
|
||||
/// used when parsing attributes during ast lowering
|
||||
pub struct Late;
|
||||
@@ -555,6 +555,25 @@ pub enum OmitDoc {
|
||||
Skip,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ShouldEmit {
|
||||
/// The operation will emit errors and lints.
|
||||
/// This is usually what you need.
|
||||
ErrorsAndLints,
|
||||
/// The operation will emit *not* errors and lints.
|
||||
/// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::Emit`.
|
||||
Nothing,
|
||||
}
|
||||
|
||||
impl ShouldEmit {
|
||||
pub fn should_emit(&self) -> bool {
|
||||
match self {
|
||||
ShouldEmit::ErrorsAndLints => true,
|
||||
ShouldEmit::Nothing => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Context created once, for example as part of the ast lowering
|
||||
/// context, through which all attributes can be lowered.
|
||||
pub struct AttributeParser<'sess, S: Stage = Late> {
|
||||
@@ -597,7 +616,7 @@ impl<'sess> AttributeParser<'sess, Early> {
|
||||
tools: Vec::new(),
|
||||
parse_only: Some(sym),
|
||||
sess,
|
||||
stage: Early { emit_errors: false },
|
||||
stage: Early { emit_errors: ShouldEmit::Nothing },
|
||||
};
|
||||
let mut parsed = p.parse_attribute_list(
|
||||
attrs,
|
||||
@@ -620,7 +639,7 @@ impl<'sess> AttributeParser<'sess, Early> {
|
||||
target_span: Span,
|
||||
target_node_id: NodeId,
|
||||
features: Option<&'sess Features>,
|
||||
emit_errors: bool,
|
||||
emit_errors: ShouldEmit,
|
||||
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T,
|
||||
template: &AttributeTemplate,
|
||||
) -> T {
|
||||
|
||||
@@ -95,7 +95,7 @@ pub use attributes::cfg_old::*;
|
||||
pub use attributes::util::{
|
||||
find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
|
||||
};
|
||||
pub use context::{AttributeParser, Early, Late, OmitDoc};
|
||||
pub use context::{AttributeParser, Early, Late, OmitDoc, ShouldEmit};
|
||||
pub use lints::emit_attribute_lint;
|
||||
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
||||
Reference in New Issue
Block a user