Skip to content

Commit

Permalink
split attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
jdonszelmann committed Dec 16, 2024
1 parent eedc229 commit 54bbce2
Show file tree
Hide file tree
Showing 30 changed files with 1,521 additions and 1,415 deletions.
Empty file added a.out
Empty file.
49 changes: 49 additions & 0 deletions compiler/rustc_attr/src/attributes/allow_unstable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use rustc_ast::attr::{AttributeExt, filter_by_name};
use rustc_session::Session;
use rustc_span::symbol::{Symbol, sym};

use crate::session_diagnostics;

pub fn allow_internal_unstable<'a>(
sess: &'a Session,
attrs: &'a [impl AttributeExt],
) -> impl Iterator<Item = Symbol> + 'a {
allow_unstable(sess, attrs, sym::allow_internal_unstable)
}

pub fn rustc_allow_const_fn_unstable<'a>(
sess: &'a Session,
attrs: &'a [impl AttributeExt],
) -> impl Iterator<Item = Symbol> + 'a {
allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
}

fn allow_unstable<'a>(
sess: &'a Session,
attrs: &'a [impl AttributeExt],
symbol: Symbol,
) -> impl Iterator<Item = Symbol> + 'a {
let attrs = filter_by_name(attrs, symbol);
let list = attrs
.filter_map(move |attr| {
attr.meta_item_list().or_else(|| {
sess.dcx().emit_err(session_diagnostics::ExpectsFeatureList {
span: attr.span(),
name: symbol.to_ident_string(),
});
None
})
})
.flatten();

list.into_iter().filter_map(move |it| {
let name = it.ident().map(|ident| ident.name);
if name.is_none() {
sess.dcx().emit_err(session_diagnostics::ExpectsFeatures {
span: it.span(),
name: symbol.to_ident_string(),
});
}
name
})
}
255 changes: 255 additions & 0 deletions compiler/rustc_attr/src/attributes/cfg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
//! Parsing and validation of builtin attributes
use rustc_ast::{self as ast, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
use rustc_ast_pretty::pprust;
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
use rustc_session::RustcVersion;
use rustc_session::Session;
use rustc_session::config::ExpectedValues;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::parse::feature_err;
use rustc_span::Span;
use rustc_span::symbol::{Symbol, kw, sym};

use crate::{fluent_generated, parse_version};
use crate::session_diagnostics;
use crate::util::UnsupportedLiteralReason;

#[derive(Clone, Debug)]
pub struct Condition {
pub name: Symbol,
pub name_span: Span,
pub value: Option<Symbol>,
pub value_span: Option<Span>,
pub span: Span,
}

/// Tests if a cfg-pattern matches the cfg set
pub fn cfg_matches(
cfg: &ast::MetaItemInner,
sess: &Session,
lint_node_id: NodeId,
features: Option<&Features>,
) -> bool {
eval_condition(cfg, sess, features, &mut |cfg| {
try_gate_cfg(cfg.name, cfg.span, sess, features);
match sess.psess.check_config.expecteds.get(&cfg.name) {
Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
sess.psess.buffer_lint(
UNEXPECTED_CFGS,
cfg.span,
lint_node_id,
BuiltinLintDiag::UnexpectedCfgValue(
(cfg.name, cfg.name_span),
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
),
);
}
None if sess.psess.check_config.exhaustive_names => {
sess.psess.buffer_lint(
UNEXPECTED_CFGS,
cfg.span,
lint_node_id,
BuiltinLintDiag::UnexpectedCfgName(
(cfg.name, cfg.name_span),
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
),
);
}
_ => { /* not unexpected */ }
}
sess.psess.config.contains(&(cfg.name, cfg.value))
})
}

fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) {
let gate = find_gated_cfg(|sym| sym == name);
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
gate_cfg(gated_cfg, span, sess, feats);
}
}

#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) {
let (cfg, feature, has_feature) = gated_cfg;
if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
let explain = format!("`cfg({cfg})` is experimental and subject to change");
feature_err(sess, *feature, cfg_span, explain).emit();
}
}

/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
/// evaluate individual items.
pub fn eval_condition(
cfg: &ast::MetaItemInner,
sess: &Session,
features: Option<&Features>,
eval: &mut impl FnMut(Condition) -> bool,
) -> bool {
let dcx = sess.dcx();

let cfg = match cfg {
ast::MetaItemInner::MetaItem(meta_item) => meta_item,
ast::MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
if let Some(features) = features {
// we can't use `try_gate_cfg` as symbols don't differentiate between `r#true`
// and `true`, and we want to keep the former working without feature gate
gate_cfg(
&(
if *b { kw::True } else { kw::False },
sym::cfg_boolean_literals,
|features: &Features| features.cfg_boolean_literals(),
),
cfg.span(),
sess,
features,
);
}
return *b;
}
_ => {
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
span: cfg.span(),
reason: UnsupportedLiteralReason::CfgBoolean,
is_bytestr: false,
start_point_span: sess.source_map().start_point(cfg.span()),
});
return false;
}
};

match &cfg.kind {
ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
try_gate_cfg(sym::version, cfg.span, sess, features);
let (min_version, span) = match &mis[..] {
[MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
(sym, span)
}
[
MetaItemInner::Lit(MetaItemLit { span, .. })
| MetaItemInner::MetaItem(MetaItem { span, .. }),
] => {
dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
return false;
}
[..] => {
dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral {
span: cfg.span,
});
return false;
}
};
let Some(min_version) = parse_version(*min_version) else {
dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span });
return false;
};

// See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
if sess.psess.assume_incomplete_release {
RustcVersion::CURRENT > min_version
} else {
RustcVersion::CURRENT >= min_version
}
}
ast::MetaItemKind::List(mis) => {
for mi in mis.iter() {
if mi.meta_item_or_bool().is_none() {
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
span: mi.span(),
reason: UnsupportedLiteralReason::Generic,
is_bytestr: false,
start_point_span: sess.source_map().start_point(mi.span()),
});
return false;
}
}

// The unwraps below may look dangerous, but we've already asserted
// that they won't fail with the loop above.
match cfg.name_or_empty() {
sym::any => mis
.iter()
// We don't use any() here, because we want to evaluate all cfg condition
// as eval_condition can (and does) extra checks
.fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)),
sym::all => mis
.iter()
// We don't use all() here, because we want to evaluate all cfg condition
// as eval_condition can (and does) extra checks
.fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)),
sym::not => {
let [mi] = mis.as_slice() else {
dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span });
return false;
};

!eval_condition(mi, sess, features, eval)
}
sym::target => {
if let Some(features) = features
&& !features.cfg_target_compact()
{
feature_err(
sess,
sym::cfg_target_compact,
cfg.span,
fluent_generated::attr_unstable_cfg_target_compact,
)
.emit();
}

mis.iter().fold(true, |res, mi| {
let Some(mut mi) = mi.meta_item().cloned() else {
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier {
span: mi.span(),
});
return false;
};

if let [seg, ..] = &mut mi.path.segments[..] {
seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
}

res & eval_condition(
&ast::MetaItemInner::MetaItem(mi),
sess,
features,
eval,
)
})
}
_ => {
dcx.emit_err(session_diagnostics::InvalidPredicate {
span: cfg.span,
predicate: pprust::path_to_string(&cfg.path),
});
false
}
}
}
ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
true
}
MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
span: lit.span,
reason: UnsupportedLiteralReason::CfgString,
is_bytestr: lit.kind.is_bytestr(),
start_point_span: sess.source_map().start_point(lit.span),
});
true
}
ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
let ident = cfg.ident().expect("multi-segment cfg predicate");
eval(Condition {
name: ident.name,
name_span: ident.span,
value: cfg.value_str(),
value_span: cfg.name_value_literal_span(),
span: cfg.span,
})
}
}
}
22 changes: 22 additions & 0 deletions compiler/rustc_attr/src/attributes/confusables.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! Parsing and validation of builtin attributes
use rustc_ast::attr::AttributeExt;
use rustc_ast::MetaItemInner;
use rustc_span::symbol::Symbol;


/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names.
pub fn parse_confusables(attr: &impl AttributeExt) -> Option<Vec<Symbol>> {
let metas = attr.meta_item_list()?;

let mut candidates = Vec::new();

for meta in metas {
let MetaItemInner::Lit(meta_lit) = meta else {
return None;
};
candidates.push(meta_lit.symbol);
}

Some(candidates)
}
Loading

0 comments on commit 54bbce2

Please sign in to comment.