Skip to content

Commit 1341366

Browse files
committed
split attributes
1 parent eedc229 commit 1341366

File tree

29 files changed

+1521
-1415
lines changed

29 files changed

+1521
-1415
lines changed

Diff for: compiler/rustc_attr/src/attributes/allow_unstable.rs

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use rustc_ast::attr::{AttributeExt, filter_by_name};
2+
use rustc_session::Session;
3+
use rustc_span::symbol::{Symbol, sym};
4+
5+
use crate::session_diagnostics;
6+
7+
pub fn allow_internal_unstable<'a>(
8+
sess: &'a Session,
9+
attrs: &'a [impl AttributeExt],
10+
) -> impl Iterator<Item = Symbol> + 'a {
11+
allow_unstable(sess, attrs, sym::allow_internal_unstable)
12+
}
13+
14+
pub fn rustc_allow_const_fn_unstable<'a>(
15+
sess: &'a Session,
16+
attrs: &'a [impl AttributeExt],
17+
) -> impl Iterator<Item = Symbol> + 'a {
18+
allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
19+
}
20+
21+
fn allow_unstable<'a>(
22+
sess: &'a Session,
23+
attrs: &'a [impl AttributeExt],
24+
symbol: Symbol,
25+
) -> impl Iterator<Item = Symbol> + 'a {
26+
let attrs = filter_by_name(attrs, symbol);
27+
let list = attrs
28+
.filter_map(move |attr| {
29+
attr.meta_item_list().or_else(|| {
30+
sess.dcx().emit_err(session_diagnostics::ExpectsFeatureList {
31+
span: attr.span(),
32+
name: symbol.to_ident_string(),
33+
});
34+
None
35+
})
36+
})
37+
.flatten();
38+
39+
list.into_iter().filter_map(move |it| {
40+
let name = it.ident().map(|ident| ident.name);
41+
if name.is_none() {
42+
sess.dcx().emit_err(session_diagnostics::ExpectsFeatures {
43+
span: it.span(),
44+
name: symbol.to_ident_string(),
45+
});
46+
}
47+
name
48+
})
49+
}

Diff for: compiler/rustc_attr/src/attributes/cfg.rs

+255
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
//! Parsing and validation of builtin attributes
2+
3+
use rustc_ast::{self as ast, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
4+
use rustc_ast_pretty::pprust;
5+
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
6+
use rustc_session::RustcVersion;
7+
use rustc_session::Session;
8+
use rustc_session::config::ExpectedValues;
9+
use rustc_session::lint::BuiltinLintDiag;
10+
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
11+
use rustc_session::parse::feature_err;
12+
use rustc_span::Span;
13+
use rustc_span::symbol::{Symbol, kw, sym};
14+
15+
use crate::{fluent_generated, parse_version};
16+
use crate::session_diagnostics;
17+
use crate::util::UnsupportedLiteralReason;
18+
19+
#[derive(Clone, Debug)]
20+
pub struct Condition {
21+
pub name: Symbol,
22+
pub name_span: Span,
23+
pub value: Option<Symbol>,
24+
pub value_span: Option<Span>,
25+
pub span: Span,
26+
}
27+
28+
/// Tests if a cfg-pattern matches the cfg set
29+
pub fn cfg_matches(
30+
cfg: &ast::MetaItemInner,
31+
sess: &Session,
32+
lint_node_id: NodeId,
33+
features: Option<&Features>,
34+
) -> bool {
35+
eval_condition(cfg, sess, features, &mut |cfg| {
36+
try_gate_cfg(cfg.name, cfg.span, sess, features);
37+
match sess.psess.check_config.expecteds.get(&cfg.name) {
38+
Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
39+
sess.psess.buffer_lint(
40+
UNEXPECTED_CFGS,
41+
cfg.span,
42+
lint_node_id,
43+
BuiltinLintDiag::UnexpectedCfgValue(
44+
(cfg.name, cfg.name_span),
45+
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
46+
),
47+
);
48+
}
49+
None if sess.psess.check_config.exhaustive_names => {
50+
sess.psess.buffer_lint(
51+
UNEXPECTED_CFGS,
52+
cfg.span,
53+
lint_node_id,
54+
BuiltinLintDiag::UnexpectedCfgName(
55+
(cfg.name, cfg.name_span),
56+
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
57+
),
58+
);
59+
}
60+
_ => { /* not unexpected */ }
61+
}
62+
sess.psess.config.contains(&(cfg.name, cfg.value))
63+
})
64+
}
65+
66+
fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) {
67+
let gate = find_gated_cfg(|sym| sym == name);
68+
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
69+
gate_cfg(gated_cfg, span, sess, feats);
70+
}
71+
}
72+
73+
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
74+
fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) {
75+
let (cfg, feature, has_feature) = gated_cfg;
76+
if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
77+
let explain = format!("`cfg({cfg})` is experimental and subject to change");
78+
feature_err(sess, *feature, cfg_span, explain).emit();
79+
}
80+
}
81+
82+
/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
83+
/// evaluate individual items.
84+
pub fn eval_condition(
85+
cfg: &ast::MetaItemInner,
86+
sess: &Session,
87+
features: Option<&Features>,
88+
eval: &mut impl FnMut(Condition) -> bool,
89+
) -> bool {
90+
let dcx = sess.dcx();
91+
92+
let cfg = match cfg {
93+
ast::MetaItemInner::MetaItem(meta_item) => meta_item,
94+
ast::MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
95+
if let Some(features) = features {
96+
// we can't use `try_gate_cfg` as symbols don't differentiate between `r#true`
97+
// and `true`, and we want to keep the former working without feature gate
98+
gate_cfg(
99+
&(
100+
if *b { kw::True } else { kw::False },
101+
sym::cfg_boolean_literals,
102+
|features: &Features| features.cfg_boolean_literals(),
103+
),
104+
cfg.span(),
105+
sess,
106+
features,
107+
);
108+
}
109+
return *b;
110+
}
111+
_ => {
112+
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
113+
span: cfg.span(),
114+
reason: UnsupportedLiteralReason::CfgBoolean,
115+
is_bytestr: false,
116+
start_point_span: sess.source_map().start_point(cfg.span()),
117+
});
118+
return false;
119+
}
120+
};
121+
122+
match &cfg.kind {
123+
ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
124+
try_gate_cfg(sym::version, cfg.span, sess, features);
125+
let (min_version, span) = match &mis[..] {
126+
[MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
127+
(sym, span)
128+
}
129+
[
130+
MetaItemInner::Lit(MetaItemLit { span, .. })
131+
| MetaItemInner::MetaItem(MetaItem { span, .. }),
132+
] => {
133+
dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
134+
return false;
135+
}
136+
[..] => {
137+
dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral {
138+
span: cfg.span,
139+
});
140+
return false;
141+
}
142+
};
143+
let Some(min_version) = parse_version(*min_version) else {
144+
dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span });
145+
return false;
146+
};
147+
148+
// See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
149+
if sess.psess.assume_incomplete_release {
150+
RustcVersion::CURRENT > min_version
151+
} else {
152+
RustcVersion::CURRENT >= min_version
153+
}
154+
}
155+
ast::MetaItemKind::List(mis) => {
156+
for mi in mis.iter() {
157+
if mi.meta_item_or_bool().is_none() {
158+
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
159+
span: mi.span(),
160+
reason: UnsupportedLiteralReason::Generic,
161+
is_bytestr: false,
162+
start_point_span: sess.source_map().start_point(mi.span()),
163+
});
164+
return false;
165+
}
166+
}
167+
168+
// The unwraps below may look dangerous, but we've already asserted
169+
// that they won't fail with the loop above.
170+
match cfg.name_or_empty() {
171+
sym::any => mis
172+
.iter()
173+
// We don't use any() here, because we want to evaluate all cfg condition
174+
// as eval_condition can (and does) extra checks
175+
.fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)),
176+
sym::all => mis
177+
.iter()
178+
// We don't use all() here, because we want to evaluate all cfg condition
179+
// as eval_condition can (and does) extra checks
180+
.fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)),
181+
sym::not => {
182+
let [mi] = mis.as_slice() else {
183+
dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span });
184+
return false;
185+
};
186+
187+
!eval_condition(mi, sess, features, eval)
188+
}
189+
sym::target => {
190+
if let Some(features) = features
191+
&& !features.cfg_target_compact()
192+
{
193+
feature_err(
194+
sess,
195+
sym::cfg_target_compact,
196+
cfg.span,
197+
fluent_generated::attr_unstable_cfg_target_compact,
198+
)
199+
.emit();
200+
}
201+
202+
mis.iter().fold(true, |res, mi| {
203+
let Some(mut mi) = mi.meta_item().cloned() else {
204+
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier {
205+
span: mi.span(),
206+
});
207+
return false;
208+
};
209+
210+
if let [seg, ..] = &mut mi.path.segments[..] {
211+
seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
212+
}
213+
214+
res & eval_condition(
215+
&ast::MetaItemInner::MetaItem(mi),
216+
sess,
217+
features,
218+
eval,
219+
)
220+
})
221+
}
222+
_ => {
223+
dcx.emit_err(session_diagnostics::InvalidPredicate {
224+
span: cfg.span,
225+
predicate: pprust::path_to_string(&cfg.path),
226+
});
227+
false
228+
}
229+
}
230+
}
231+
ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
232+
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
233+
true
234+
}
235+
MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
236+
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
237+
span: lit.span,
238+
reason: UnsupportedLiteralReason::CfgString,
239+
is_bytestr: lit.kind.is_bytestr(),
240+
start_point_span: sess.source_map().start_point(lit.span),
241+
});
242+
true
243+
}
244+
ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
245+
let ident = cfg.ident().expect("multi-segment cfg predicate");
246+
eval(Condition {
247+
name: ident.name,
248+
name_span: ident.span,
249+
value: cfg.value_str(),
250+
value_span: cfg.name_value_literal_span(),
251+
span: cfg.span,
252+
})
253+
}
254+
}
255+
}

Diff for: compiler/rustc_attr/src/attributes/confusables.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//! Parsing and validation of builtin attributes
2+
3+
use rustc_ast::attr::AttributeExt;
4+
use rustc_ast::MetaItemInner;
5+
use rustc_span::symbol::Symbol;
6+
7+
8+
/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names.
9+
pub fn parse_confusables(attr: &impl AttributeExt) -> Option<Vec<Symbol>> {
10+
let metas = attr.meta_item_list()?;
11+
12+
let mut candidates = Vec::new();
13+
14+
for meta in metas {
15+
let MetaItemInner::Lit(meta_lit) = meta else {
16+
return None;
17+
};
18+
candidates.push(meta_lit.symbol);
19+
}
20+
21+
Some(candidates)
22+
}

0 commit comments

Comments
 (0)