Skip to content

Commit 8e400f9

Browse files
Fix ice for feature-gated cfg attributes applied to the crate
Signed-off-by: Jonathan Brouwer <jonathantbrouwer@gmail.com>
1 parent e27f16a commit 8e400f9

File tree

6 files changed

+74
-38
lines changed

6 files changed

+74
-38
lines changed

compiler/rustc_attr_parsing/src/attributes/cfg.rs

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_session::parse::feature_err;
99
use rustc_span::{Span, Symbol, sym};
1010
use thin_vec::ThinVec;
1111

12-
use crate::context::{AcceptContext, Stage};
12+
use crate::context::{AcceptContext, ShouldEmit, Stage};
1313
use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser};
1414
use crate::{
1515
CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg,
@@ -90,7 +90,7 @@ fn parse_cfg_entry_version<S: Stage>(
9090
list: &MetaItemListParser<'_>,
9191
meta_span: Span,
9292
) -> Option<CfgEntry> {
93-
try_gate_cfg(sym::version, meta_span, cx.sess(), Some(cx.features()));
93+
try_gate_cfg(sym::version, meta_span, cx.sess(), cx.features_option());
9494
let Some(version) = list.single() else {
9595
cx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: list.span });
9696
return None;
@@ -119,7 +119,9 @@ fn parse_cfg_entry_target<S: Stage>(
119119
list: &MetaItemListParser<'_>,
120120
meta_span: Span,
121121
) -> Option<CfgEntry> {
122-
if !cx.features().cfg_target_compact() {
122+
if let Some(features) = cx.features_option()
123+
&& !features.cfg_target_compact()
124+
{
123125
feature_err(
124126
cx.sess(),
125127
sym::cfg_target_compact,
@@ -186,12 +188,13 @@ pub fn eval_config_entry(
186188
cfg_entry: &CfgEntry,
187189
id: NodeId,
188190
features: Option<&Features>,
191+
emit_lints: ShouldEmit,
189192
) -> EvalConfigResult {
190193
match cfg_entry {
191194
CfgEntry::All(subs, ..) => {
192195
let mut all = None;
193196
for sub in subs {
194-
let res = eval_config_entry(sess, sub, id, features);
197+
let res = eval_config_entry(sess, sub, id, features, emit_lints);
195198
// We cannot short-circuit because `eval_config_entry` emits some lints
196199
if !res.as_bool() {
197200
all.get_or_insert(res);
@@ -202,7 +205,7 @@ pub fn eval_config_entry(
202205
CfgEntry::Any(subs, span) => {
203206
let mut any = None;
204207
for sub in subs {
205-
let res = eval_config_entry(sess, sub, id, features);
208+
let res = eval_config_entry(sess, sub, id, features, emit_lints);
206209
// We cannot short-circuit because `eval_config_entry` emits some lints
207210
if res.as_bool() {
208211
any.get_or_insert(res);
@@ -214,7 +217,7 @@ pub fn eval_config_entry(
214217
})
215218
}
216219
CfgEntry::Not(sub, span) => {
217-
if eval_config_entry(sess, sub, id, features).as_bool() {
220+
if eval_config_entry(sess, sub, id, features, emit_lints).as_bool() {
218221
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
219222
} else {
220223
EvalConfigResult::True
@@ -228,24 +231,28 @@ pub fn eval_config_entry(
228231
}
229232
}
230233
CfgEntry::NameValue { name, name_span, value, span } => {
231-
match sess.psess.check_config.expecteds.get(name) {
232-
Some(ExpectedValues::Some(values)) if !values.contains(&value.map(|(v, _)| v)) => {
233-
id.emit_span_lint(
234-
sess,
235-
UNEXPECTED_CFGS,
236-
*span,
237-
BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value),
238-
);
234+
if let ShouldEmit::ErrorsAndLints = emit_lints {
235+
match sess.psess.check_config.expecteds.get(name) {
236+
Some(ExpectedValues::Some(values))
237+
if !values.contains(&value.map(|(v, _)| v)) =>
238+
{
239+
id.emit_span_lint(
240+
sess,
241+
UNEXPECTED_CFGS,
242+
*span,
243+
BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value),
244+
);
245+
}
246+
None if sess.psess.check_config.exhaustive_names => {
247+
id.emit_span_lint(
248+
sess,
249+
UNEXPECTED_CFGS,
250+
*span,
251+
BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value),
252+
);
253+
}
254+
_ => { /* not unexpected */ }
239255
}
240-
None if sess.psess.check_config.exhaustive_names => {
241-
id.emit_span_lint(
242-
sess,
243-
UNEXPECTED_CFGS,
244-
*span,
245-
BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value),
246-
);
247-
}
248-
_ => { /* not unexpected */ }
249256
}
250257

251258
if sess.psess.config.contains(&(*name, value.map(|(v, _)| v))) {

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ impl Stage for Early {
223223
sess: &'sess Session,
224224
diag: impl for<'x> Diagnostic<'x>,
225225
) -> ErrorGuaranteed {
226-
if self.emit_errors {
226+
if self.emit_errors.should_emit() {
227227
sess.dcx().emit_err(diag)
228228
} else {
229229
sess.dcx().create_err(diag).delay_as_bug()
@@ -254,7 +254,7 @@ pub struct Early {
254254
/// Whether to emit errors or delay them as a bug
255255
/// For most attributes, the attribute will be parsed again in the `Late` stage and in this case the errors should be delayed
256256
/// But for some, such as `cfg`, the attribute will be removed before the `Late` stage so errors must be emitted
257-
pub emit_errors: bool,
257+
pub emit_errors: ShouldEmit,
258258
}
259259
/// used when parsing attributes during ast lowering
260260
pub struct Late;
@@ -555,6 +555,25 @@ pub enum OmitDoc {
555555
Skip,
556556
}
557557

558+
#[derive(Copy, Clone)]
559+
pub enum ShouldEmit {
560+
/// The operation will emit errors and lints.
561+
/// This is usually what you need.
562+
ErrorsAndLints,
563+
/// The operation will emit *not* errors and lints.
564+
/// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::Emit`.
565+
Nothing,
566+
}
567+
568+
impl ShouldEmit {
569+
pub fn should_emit(&self) -> bool {
570+
match self {
571+
ShouldEmit::ErrorsAndLints => true,
572+
ShouldEmit::Nothing => false,
573+
}
574+
}
575+
}
576+
558577
/// Context created once, for example as part of the ast lowering
559578
/// context, through which all attributes can be lowered.
560579
pub struct AttributeParser<'sess, S: Stage = Late> {
@@ -597,7 +616,7 @@ impl<'sess> AttributeParser<'sess, Early> {
597616
tools: Vec::new(),
598617
parse_only: Some(sym),
599618
sess,
600-
stage: Early { emit_errors: false },
619+
stage: Early { emit_errors: ShouldEmit::Nothing },
601620
};
602621
let mut parsed = p.parse_attribute_list(
603622
attrs,
@@ -620,7 +639,7 @@ impl<'sess> AttributeParser<'sess, Early> {
620639
target_span: Span,
621640
target_node_id: NodeId,
622641
features: Option<&'sess Features>,
623-
emit_errors: bool,
642+
emit_errors: ShouldEmit,
624643
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T,
625644
template: &AttributeTemplate,
626645
) -> T {

compiler/rustc_attr_parsing/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ pub use attributes::cfg_old::*;
9595
pub use attributes::util::{
9696
find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
9797
};
98-
pub use context::{AttributeParser, Early, Late, OmitDoc};
98+
pub use context::{AttributeParser, Early, Late, OmitDoc, ShouldEmit};
9999
pub use lints::emit_attribute_lint;
100100

101101
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }

compiler/rustc_expand/src/config.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_ast::{
1212
};
1313
use rustc_attr_parsing as attr;
1414
use rustc_attr_parsing::{
15-
AttributeParser, CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr,
15+
AttributeParser, CFG_TEMPLATE, EvalConfigResult, ShouldEmit, eval_config_entry, parse_cfg_attr,
1616
};
1717
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
1818
use rustc_feature::{
@@ -167,7 +167,9 @@ pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec
167167
.flat_map(|attr| strip_unconfigured.process_cfg_attr(attr))
168168
.take_while(|attr| {
169169
!is_cfg(attr)
170-
|| strip_unconfigured.cfg_true(attr, strip_unconfigured.lint_node_id).as_bool()
170+
|| strip_unconfigured
171+
.cfg_true(attr, strip_unconfigured.lint_node_id, ShouldEmit::Nothing)
172+
.as_bool()
171173
})
172174
.collect()
173175
}
@@ -401,10 +403,18 @@ impl<'a> StripUnconfigured<'a> {
401403

402404
/// Determines if a node with the given attributes should be included in this configuration.
403405
fn in_cfg(&self, attrs: &[Attribute]) -> bool {
404-
attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr, self.lint_node_id).as_bool())
406+
attrs.iter().all(|attr| {
407+
!is_cfg(attr)
408+
|| self.cfg_true(attr, self.lint_node_id, ShouldEmit::ErrorsAndLints).as_bool()
409+
})
405410
}
406411

407-
pub(crate) fn cfg_true(&self, attr: &Attribute, node: NodeId) -> EvalConfigResult {
412+
pub(crate) fn cfg_true(
413+
&self,
414+
attr: &Attribute,
415+
node: NodeId,
416+
emit_errors: ShouldEmit,
417+
) -> EvalConfigResult {
408418
// We need to run this to do basic validation of the attribute, such as that lits are valid, etc
409419
// FIXME(jdonszelmann) this should not be necessary in the future
410420
match validate_attr::parse_meta(&self.sess.psess, attr) {
@@ -428,15 +438,15 @@ impl<'a> StripUnconfigured<'a> {
428438
attr.span,
429439
node,
430440
self.features,
431-
true,
441+
emit_errors,
432442
parse_cfg_attr,
433443
&CFG_TEMPLATE,
434444
) else {
435445
// Cfg attribute was not parsable, give up
436446
return EvalConfigResult::True;
437447
};
438448

439-
eval_config_entry(self.sess, &cfg, self.lint_node_id, self.features)
449+
eval_config_entry(self.sess, &cfg, self.lint_node_id, self.features, emit_errors)
440450
}
441451

442452
/// If attributes are not allowed on expressions, emit an error for `attr`

compiler/rustc_expand/src/expand.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_ast::{
1313
MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token,
1414
};
1515
use rustc_ast_pretty::pprust;
16-
use rustc_attr_parsing::EvalConfigResult;
16+
use rustc_attr_parsing::{EvalConfigResult, ShouldEmit};
1717
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
1818
use rustc_errors::PResult;
1919
use rustc_feature::Features;
@@ -2171,7 +2171,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
21712171
attr: ast::Attribute,
21722172
pos: usize,
21732173
) -> EvalConfigResult {
2174-
let res = self.cfg().cfg_true(&attr, node.node_id());
2174+
let res = self.cfg().cfg_true(&attr, node.node_id(), ShouldEmit::ErrorsAndLints);
21752175
if res.as_bool() {
21762176
// A trace attribute left in AST in place of the original `cfg` attribute.
21772177
// It can later be used by lints or other diagnostics.

compiler/rustc_resolve/src/def_collector.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::mem;
22

33
use rustc_ast::visit::FnKind;
44
use rustc_ast::*;
5-
use rustc_attr_parsing::{AttributeParser, Early, OmitDoc};
5+
use rustc_attr_parsing::{AttributeParser, Early, OmitDoc, ShouldEmit};
66
use rustc_expand::expand::AstFragment;
77
use rustc_hir as hir;
88
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
@@ -132,7 +132,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
132132
&self.resolver.tcx.sess,
133133
self.resolver.tcx.features(),
134134
Vec::new(),
135-
Early { emit_errors: false },
135+
Early { emit_errors: ShouldEmit::Nothing },
136136
);
137137
let attrs = parser.parse_attribute_list(
138138
&i.attrs,

0 commit comments

Comments
 (0)