mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
introduce new lint infra
lint on duplicates during attribute parsing To do this we stuff them in the diagnostic context to be emitted after hir is constructed
This commit is contained in:
@@ -3787,6 +3787,7 @@ dependencies = [
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
"rustc_attr_data_structures",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
|
||||
@@ -73,16 +73,15 @@ pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> {
|
||||
// Merge attributes into the inner expression.
|
||||
if !e.attrs.is_empty() {
|
||||
let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]);
|
||||
let attrs = &*self.arena.alloc_from_iter(
|
||||
self.lower_attrs_vec(&e.attrs, e.span)
|
||||
.into_iter()
|
||||
.chain(old_attrs.iter().cloned()),
|
||||
);
|
||||
if attrs.is_empty() {
|
||||
let new_attrs = self
|
||||
.lower_attrs_vec(&e.attrs, e.span, ex.hir_id)
|
||||
.into_iter()
|
||||
.chain(old_attrs.iter().cloned());
|
||||
let new_attrs = &*self.arena.alloc_from_iter(new_attrs);
|
||||
if new_attrs.is_empty() {
|
||||
return ex;
|
||||
}
|
||||
|
||||
self.attrs.insert(ex.hir_id.local_id, attrs);
|
||||
self.attrs.insert(ex.hir_id.local_id, new_attrs);
|
||||
}
|
||||
return ex;
|
||||
}
|
||||
@@ -2035,7 +2034,7 @@ fn lower_expr_try(&mut self, span: Span, sub_expr: &Expr) -> hir::ExprKind<'hir>
|
||||
let ret_expr = self.checked_return(Some(from_residual_expr));
|
||||
self.arena.alloc(self.expr(try_span, ret_expr))
|
||||
};
|
||||
self.lower_attrs(ret_expr.hir_id, &attrs, ret_expr.span);
|
||||
self.lower_attrs(ret_expr.hir_id, &attrs, span);
|
||||
|
||||
let break_pat = self.pat_cf_break(try_span, residual_local);
|
||||
self.arm(break_pat, ret_expr)
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
|
||||
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_hir::lints::DelayedLint;
|
||||
use rustc_hir::{
|
||||
self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem,
|
||||
LifetimeSource, LifetimeSyntax, ParamName, TraitCandidate,
|
||||
@@ -141,6 +142,8 @@ struct LoweringContext<'a, 'hir> {
|
||||
allow_for_await: Arc<[Symbol]>,
|
||||
allow_async_fn_traits: Arc<[Symbol]>,
|
||||
|
||||
delayed_lints: Vec<DelayedLint>,
|
||||
|
||||
attribute_parser: AttributeParser<'hir>,
|
||||
}
|
||||
|
||||
@@ -190,6 +193,7 @@ fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
|
||||
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
|
||||
|
||||
attribute_parser: AttributeParser::new(tcx.sess, tcx.features(), registered_tools),
|
||||
delayed_lints: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +202,22 @@ pub(crate) fn dcx(&self) -> DiagCtxtHandle<'hir> {
|
||||
}
|
||||
}
|
||||
|
||||
struct SpanLowerer {
|
||||
is_incremental: bool,
|
||||
def_id: LocalDefId,
|
||||
}
|
||||
|
||||
impl SpanLowerer {
|
||||
fn lower(&self, span: Span) -> Span {
|
||||
if self.is_incremental {
|
||||
span.with_parent(Some(self.def_id))
|
||||
} else {
|
||||
// Do not make spans relative when not using incremental compilation.
|
||||
span
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[extension(trait ResolverAstLoweringExt)]
|
||||
impl ResolverAstLowering {
|
||||
fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>> {
|
||||
@@ -573,6 +593,7 @@ fn with_hir_id_owner(
|
||||
std::mem::replace(&mut self.item_local_id_counter, hir::ItemLocalId::new(1));
|
||||
let current_impl_trait_defs = std::mem::take(&mut self.impl_trait_defs);
|
||||
let current_impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds);
|
||||
let current_delayed_lints = std::mem::take(&mut self.delayed_lints);
|
||||
|
||||
// Do not reset `next_node_id` and `node_id_to_def_id`:
|
||||
// we want `f` to be able to refer to the `LocalDefId`s that the caller created.
|
||||
@@ -606,6 +627,7 @@ fn with_hir_id_owner(
|
||||
self.item_local_id_counter = current_local_counter;
|
||||
self.impl_trait_defs = current_impl_trait_defs;
|
||||
self.impl_trait_bounds = current_impl_trait_bounds;
|
||||
self.delayed_lints = current_delayed_lints;
|
||||
|
||||
debug_assert!(!self.children.iter().any(|(id, _)| id == &owner_id.def_id));
|
||||
self.children.push((owner_id.def_id, hir::MaybeOwner::Owner(info)));
|
||||
@@ -616,6 +638,7 @@ fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInf
|
||||
let mut bodies = std::mem::take(&mut self.bodies);
|
||||
let define_opaque = std::mem::take(&mut self.define_opaque);
|
||||
let trait_map = std::mem::take(&mut self.trait_map);
|
||||
let delayed_lints = std::mem::take(&mut self.delayed_lints).into_boxed_slice();
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
for (id, attrs) in attrs.iter() {
|
||||
@@ -629,14 +652,16 @@ fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInf
|
||||
let bodies = SortedMap::from_presorted_elements(bodies);
|
||||
|
||||
// Don't hash unless necessary, because it's expensive.
|
||||
let (opt_hash_including_bodies, attrs_hash) =
|
||||
self.tcx.hash_owner_nodes(node, &bodies, &attrs, define_opaque);
|
||||
let (opt_hash_including_bodies, attrs_hash, delayed_lints_hash) =
|
||||
self.tcx.hash_owner_nodes(node, &bodies, &attrs, &delayed_lints, define_opaque);
|
||||
let num_nodes = self.item_local_id_counter.as_usize();
|
||||
let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes);
|
||||
let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies };
|
||||
let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash, define_opaque };
|
||||
let delayed_lints =
|
||||
hir::lints::DelayedLints { lints: delayed_lints, opt_hash: delayed_lints_hash };
|
||||
|
||||
self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
|
||||
self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map, delayed_lints })
|
||||
}
|
||||
|
||||
/// This method allocates a new `HirId` for the given `NodeId`.
|
||||
@@ -759,15 +784,17 @@ fn mark_span_with_reason(
|
||||
})
|
||||
}
|
||||
|
||||
fn span_lowerer(&self) -> SpanLowerer {
|
||||
SpanLowerer {
|
||||
is_incremental: self.tcx.sess.opts.incremental.is_some(),
|
||||
def_id: self.current_hir_id_owner.def_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Intercept all spans entering HIR.
|
||||
/// Mark a span as relative to the current owning item.
|
||||
fn lower_span(&self, span: Span) -> Span {
|
||||
if self.tcx.sess.opts.incremental.is_some() {
|
||||
span.with_parent(Some(self.current_hir_id_owner.def_id))
|
||||
} else {
|
||||
// Do not make spans relative when not using incremental compilation.
|
||||
span
|
||||
}
|
||||
self.span_lowerer().lower(span)
|
||||
}
|
||||
|
||||
fn lower_ident(&self, ident: Ident) -> Ident {
|
||||
@@ -889,7 +916,7 @@ fn lower_attrs(
|
||||
if attrs.is_empty() {
|
||||
&[]
|
||||
} else {
|
||||
let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span));
|
||||
let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span), id);
|
||||
|
||||
debug_assert_eq!(id.owner, self.current_hir_id_owner);
|
||||
let ret = self.arena.alloc_from_iter(lowered_attrs);
|
||||
@@ -909,9 +936,23 @@ fn lower_attrs(
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec<hir::Attribute> {
|
||||
self.attribute_parser
|
||||
.parse_attribute_list(attrs, target_span, OmitDoc::Lower, |s| self.lower_span(s))
|
||||
fn lower_attrs_vec(
|
||||
&mut self,
|
||||
attrs: &[Attribute],
|
||||
target_span: Span,
|
||||
target_hir_id: HirId,
|
||||
) -> Vec<hir::Attribute> {
|
||||
let l = self.span_lowerer();
|
||||
self.attribute_parser.parse_attribute_list(
|
||||
attrs,
|
||||
target_span,
|
||||
target_hir_id,
|
||||
OmitDoc::Lower,
|
||||
|s| l.lower(s),
|
||||
|l| {
|
||||
self.delayed_lints.push(DelayedLint::AttributeParsing(l));
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn alias_attrs(&mut self, id: HirId, target_id: HirId) {
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
mod stability;
|
||||
mod version;
|
||||
|
||||
pub mod lints;
|
||||
|
||||
use std::num::NonZero;
|
||||
|
||||
pub use attributes::*;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
use rustc_macros::HashStable_Generic;
|
||||
use rustc_span::Span;
|
||||
|
||||
#[derive(Clone, Debug, HashStable_Generic)]
|
||||
pub struct AttributeLint<Id> {
|
||||
pub id: Id,
|
||||
pub span: Span,
|
||||
pub kind: AttributeLintKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable_Generic)]
|
||||
pub enum AttributeLintKind {
|
||||
UnusedDuplicate { this: Span, other: Span, warning: bool },
|
||||
}
|
||||
@@ -131,7 +131,17 @@ attr_parsing_unsupported_literal_generic =
|
||||
attr_parsing_unsupported_literal_suggestion =
|
||||
consider removing the prefix
|
||||
|
||||
attr_parsing_unused_duplicate =
|
||||
unused attribute
|
||||
.suggestion = remove this attribute
|
||||
.note = attribute also specified here
|
||||
.warn = {-attr_parsing_previously_accepted}
|
||||
|
||||
|
||||
attr_parsing_unused_multiple =
|
||||
multiple `{$name}` attributes
|
||||
.suggestion = remove this attribute
|
||||
.note = attribute also specified here
|
||||
|
||||
-attr_parsing_perviously_accepted =
|
||||
this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
|
||||
@@ -4,41 +4,43 @@
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::{CombineAttributeParser, ConvertFn};
|
||||
use crate::context::AcceptContext;
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
|
||||
pub(crate) struct AllowInternalUnstableParser;
|
||||
impl CombineAttributeParser for AllowInternalUnstableParser {
|
||||
impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
|
||||
const PATH: &'static [Symbol] = &[sym::allow_internal_unstable];
|
||||
type Item = (Symbol, Span);
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
|
||||
|
||||
fn extend<'a>(
|
||||
cx: &'a AcceptContext<'a>,
|
||||
args: &'a ArgParser<'a>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'a {
|
||||
parse_unstable(cx, args, Self::PATH[0]).into_iter().zip(iter::repeat(cx.attr_span))
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> {
|
||||
parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
|
||||
.into_iter()
|
||||
.zip(iter::repeat(cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct AllowConstFnUnstableParser;
|
||||
impl CombineAttributeParser for AllowConstFnUnstableParser {
|
||||
impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_allow_const_fn_unstable];
|
||||
type Item = Symbol;
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
|
||||
|
||||
fn extend<'a>(
|
||||
cx: &'a AcceptContext<'a>,
|
||||
args: &'a ArgParser<'a>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'a {
|
||||
parse_unstable(cx, args, Self::PATH[0])
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
||||
parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_unstable<'a>(
|
||||
cx: &AcceptContext<'_>,
|
||||
args: &'a ArgParser<'a>,
|
||||
fn parse_unstable<S: Stage>(
|
||||
cx: &AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser<'_>,
|
||||
symbol: Symbol,
|
||||
) -> impl IntoIterator<Item = Symbol> {
|
||||
let mut res = Vec::new();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use super::{AcceptMapping, AttributeParser};
|
||||
use crate::context::FinalizeContext;
|
||||
use crate::context::{FinalizeContext, Stage};
|
||||
use crate::session_diagnostics;
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -12,8 +12,8 @@ pub(crate) struct ConfusablesParser {
|
||||
first_span: Option<Span>,
|
||||
}
|
||||
|
||||
impl AttributeParser for ConfusablesParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self> = &[(&[sym::rustc_confusables], |this, cx, args| {
|
||||
impl<S: Stage> AttributeParser<S> for ConfusablesParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(&[sym::rustc_confusables], |this, cx, args| {
|
||||
let Some(list) = args.list() else {
|
||||
// FIXME(jdonszelmann): error when not a list? Bring validation code here.
|
||||
// NOTE: currently subsequent attributes are silently ignored using
|
||||
@@ -45,7 +45,7 @@ impl AttributeParser for ConfusablesParser {
|
||||
this.first_span.get_or_insert(cx.attr_span);
|
||||
})];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if self.confusables.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::util::parse_version;
|
||||
use super::{AttributeDuplicates, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::AcceptContext;
|
||||
use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
use crate::session_diagnostics::UnsupportedLiteralReason;
|
||||
|
||||
pub(crate) struct DeprecationParser;
|
||||
|
||||
fn get(
|
||||
cx: &AcceptContext<'_>,
|
||||
fn get<S: Stage>(
|
||||
cx: &AcceptContext<'_, '_, S>,
|
||||
name: Symbol,
|
||||
param_span: Span,
|
||||
arg: &ArgParser<'_>,
|
||||
@@ -41,12 +41,12 @@ fn get(
|
||||
}
|
||||
}
|
||||
|
||||
impl SingleAttributeParser for DeprecationParser {
|
||||
impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
|
||||
const PATH: &'static [Symbol] = &[sym::deprecated];
|
||||
const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing;
|
||||
const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error;
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
|
||||
fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let features = cx.features();
|
||||
|
||||
let mut since = None;
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// FIXME(jdonszelmann): merge these two parsers and error when both attributes are present here.
|
||||
// note: need to model better how duplicate attr errors work when not using
|
||||
// SingleAttributeParser which is what we have two of here.
|
||||
|
||||
use rustc_attr_data_structures::lints::AttributeLintKind;
|
||||
use rustc_attr_data_structures::{AttributeKind, InlineAttr};
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use super::{AcceptContext, AttributeOrder, OnDuplicate};
|
||||
use crate::attributes::SingleAttributeParser;
|
||||
use crate::context::Stage;
|
||||
use crate::parser::ArgParser;
|
||||
|
||||
pub(crate) struct InlineParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for InlineParser {
|
||||
const PATH: &'static [Symbol] = &[sym::inline];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: "always|never");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
match args {
|
||||
ArgParser::NoArgs => Some(AttributeKind::Inline(InlineAttr::Hint, cx.attr_span)),
|
||||
ArgParser::List(list) => {
|
||||
let Some(l) = list.single() else {
|
||||
cx.expected_single_argument(list.span);
|
||||
return None;
|
||||
};
|
||||
|
||||
match l.meta_item().and_then(|i| i.word_without_args().map(|i| i.name)) {
|
||||
Some(sym::always) => {
|
||||
Some(AttributeKind::Inline(InlineAttr::Always, cx.attr_span))
|
||||
}
|
||||
Some(sym::never) => {
|
||||
Some(AttributeKind::Inline(InlineAttr::Never, cx.attr_span))
|
||||
}
|
||||
_ => {
|
||||
cx.expected_specific_argument(l.span(), vec!["always", "never"]);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
ArgParser::NameValue(_) => {
|
||||
let suggestions =
|
||||
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "inline");
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::IllFormedAttributeInput { suggestions },
|
||||
cx.attr_span,
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustcForceInlineParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_force_inline];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: "reason", NameValueStr: "reason");
|
||||
|
||||
fn convert(cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let reason = match args {
|
||||
ArgParser::NoArgs => None,
|
||||
ArgParser::List(list) => {
|
||||
let Some(l) = list.single() else {
|
||||
cx.expected_single_argument(list.span);
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(reason) = l.lit().and_then(|i| i.kind.str()) else {
|
||||
cx.expected_string_literal(l.span());
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(reason)
|
||||
}
|
||||
ArgParser::NameValue(v) => {
|
||||
let Some(reason) = v.value_as_str() else {
|
||||
cx.expected_string_literal(v.value_span);
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(reason)
|
||||
}
|
||||
};
|
||||
|
||||
Some(AttributeKind::Inline(
|
||||
InlineAttr::Force { attr_span: cx.attr_span, reason },
|
||||
cx.attr_span,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,11 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_attr_data_structures::lints::AttributeLintKind;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::context::{AcceptContext, FinalizeContext};
|
||||
use crate::context::{AcceptContext, FinalizeContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics::UnusedMultiple;
|
||||
|
||||
@@ -33,8 +34,8 @@
|
||||
pub(crate) mod transparency;
|
||||
pub(crate) mod util;
|
||||
|
||||
type AcceptFn<T> = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>);
|
||||
type AcceptMapping<T> = &'static [(&'static [Symbol], AcceptFn<T>)];
|
||||
type AcceptFn<T, S> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser<'_>);
|
||||
type AcceptMapping<T, S> = &'static [(&'static [Symbol], AcceptFn<T, S>)];
|
||||
|
||||
/// An [`AttributeParser`] is a type which searches for syntactic attributes.
|
||||
///
|
||||
@@ -55,11 +56,11 @@
|
||||
///
|
||||
/// For a simpler attribute parsing interface, consider using [`SingleAttributeParser`]
|
||||
/// or [`CombineAttributeParser`] instead.
|
||||
pub(crate) trait AttributeParser: Default + 'static {
|
||||
pub(crate) trait AttributeParser<S: Stage>: Default + 'static {
|
||||
/// The symbols for the attributes that this parser is interested in.
|
||||
///
|
||||
/// If an attribute has this symbol, the `accept` function will be called on it.
|
||||
const ATTRIBUTES: AcceptMapping<Self>;
|
||||
const ATTRIBUTES: AcceptMapping<Self, S>;
|
||||
|
||||
/// The parser has gotten a chance to accept the attributes on an item,
|
||||
/// here it can produce an attribute.
|
||||
@@ -69,7 +70,7 @@ pub(crate) trait AttributeParser: Default + 'static {
|
||||
/// that'd be equivalent to unconditionally applying an attribute to
|
||||
/// every single syntax item that could have attributes applied to it.
|
||||
/// Your accept mappings should determine whether this returns something.
|
||||
fn finalize(self, cx: &FinalizeContext<'_>) -> Option<AttributeKind>;
|
||||
fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind>;
|
||||
}
|
||||
|
||||
/// Alternative to [`AttributeParser`] that automatically handles state management.
|
||||
@@ -81,54 +82,60 @@ pub(crate) trait AttributeParser: Default + 'static {
|
||||
///
|
||||
/// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple
|
||||
/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
|
||||
pub(crate) trait SingleAttributeParser: 'static {
|
||||
pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
|
||||
const PATH: &'static [Symbol];
|
||||
|
||||
const ON_DUPLICATE_STRATEGY: AttributeDuplicates;
|
||||
const ON_DUPLICATE: OnDuplicate;
|
||||
const ATTRIBUTE_ORDER: AttributeOrder;
|
||||
const ON_DUPLICATE: OnDuplicate<S>;
|
||||
|
||||
/// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
|
||||
fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind>;
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>;
|
||||
}
|
||||
|
||||
pub(crate) struct Single<T: SingleAttributeParser>(PhantomData<T>, Option<(AttributeKind, Span)>);
|
||||
pub(crate) struct Single<T: SingleAttributeParser<S>, S: Stage>(
|
||||
PhantomData<(S, T)>,
|
||||
Option<(AttributeKind, Span)>,
|
||||
);
|
||||
|
||||
impl<T: SingleAttributeParser> Default for Single<T> {
|
||||
impl<T: SingleAttributeParser<S>, S: Stage> Default for Single<T, S> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default(), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SingleAttributeParser> AttributeParser for Single<T> {
|
||||
const ATTRIBUTES: AcceptMapping<Self> = &[(T::PATH, |group: &mut Single<T>, cx, args| {
|
||||
if let Some(pa) = T::convert(cx, args) {
|
||||
match T::ON_DUPLICATE_STRATEGY {
|
||||
// keep the first and error
|
||||
AttributeDuplicates::ErrorFollowing => {
|
||||
if let Some((_, unused)) = group.1 {
|
||||
T::ON_DUPLICATE.exec::<T>(cx, cx.attr_span, unused);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// keep the new one and warn about the previous,
|
||||
// then replace
|
||||
AttributeDuplicates::FutureWarnPreceding => {
|
||||
if let Some((_, used)) = group.1 {
|
||||
T::ON_DUPLICATE.exec::<T>(cx, used, cx.attr_span);
|
||||
impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S> {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> =
|
||||
&[(T::PATH, |group: &mut Single<T, S>, cx, args| {
|
||||
if let Some(pa) = T::convert(cx, args) {
|
||||
match T::ATTRIBUTE_ORDER {
|
||||
// keep the first and report immediately. ignore this attribute
|
||||
AttributeOrder::KeepFirst => {
|
||||
if let Some((_, unused)) = group.1 {
|
||||
T::ON_DUPLICATE.exec::<T>(cx, cx.attr_span, unused);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// keep the new one and warn about the previous,
|
||||
// then replace
|
||||
AttributeOrder::KeepLast => {
|
||||
if let Some((_, used)) = group.1 {
|
||||
T::ON_DUPLICATE.exec::<T>(cx, used, cx.attr_span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group.1 = Some((pa, cx.attr_span));
|
||||
}
|
||||
})];
|
||||
|
||||
group.1 = Some((pa, cx.attr_span));
|
||||
}
|
||||
})];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
Some(self.1?.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum OnDuplicate {
|
||||
// FIXME(jdonszelmann): logic is implemented but the attribute parsers needing
|
||||
// them will be merged in another PR
|
||||
#[allow(unused)]
|
||||
pub(crate) enum OnDuplicate<S: Stage> {
|
||||
/// Give a default warning
|
||||
Warn,
|
||||
|
||||
@@ -146,18 +153,25 @@ pub(crate) enum OnDuplicate {
|
||||
/// - `unused` is the span of the attribute that was unused or bad because of some
|
||||
/// duplicate reason (see [`AttributeDuplicates`])
|
||||
/// - `used` is the span of the attribute that was used in favor of the unused attribute
|
||||
Custom(fn(cx: &AcceptContext<'_>, used: Span, unused: Span)),
|
||||
Custom(fn(cx: &AcceptContext<'_, '_, S>, used: Span, unused: Span)),
|
||||
}
|
||||
|
||||
impl OnDuplicate {
|
||||
fn exec<P: SingleAttributeParser>(&self, cx: &AcceptContext<'_>, used: Span, unused: Span) {
|
||||
impl<S: Stage> OnDuplicate<S> {
|
||||
fn exec<P: SingleAttributeParser<S>>(
|
||||
&self,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
used: Span,
|
||||
unused: Span,
|
||||
) {
|
||||
match self {
|
||||
OnDuplicate::Warn => {
|
||||
todo!()
|
||||
}
|
||||
OnDuplicate::WarnButFutureError => {
|
||||
todo!()
|
||||
}
|
||||
OnDuplicate::Warn => cx.emit_lint(
|
||||
AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: false },
|
||||
unused,
|
||||
),
|
||||
OnDuplicate::WarnButFutureError => cx.emit_lint(
|
||||
AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: true },
|
||||
unused,
|
||||
),
|
||||
OnDuplicate::Error => {
|
||||
cx.emit_err(UnusedMultiple {
|
||||
this: used,
|
||||
@@ -172,14 +186,17 @@ fn exec<P: SingleAttributeParser>(&self, cx: &AcceptContext<'_>, used: Span, unu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum AttributeDuplicates {
|
||||
//
|
||||
// FIXME(jdonszelmann): logic is implemented but the attribute parsers needing
|
||||
// them will be merged in another PR
|
||||
#[allow(unused)]
|
||||
pub(crate) enum AttributeOrder {
|
||||
/// Duplicates after the first attribute will be an error.
|
||||
///
|
||||
/// This should be used where duplicates would be ignored, but carry extra
|
||||
/// meaning that could cause confusion. For example, `#[stable(since="1.0")]
|
||||
/// #[stable(since="2.0")]`, which version should be used for `stable`?
|
||||
ErrorFollowing,
|
||||
KeepFirst,
|
||||
|
||||
/// Duplicates preceding the last instance of the attribute will be a
|
||||
/// warning, with a note that this will be an error in the future.
|
||||
@@ -187,7 +204,7 @@ pub(crate) enum AttributeDuplicates {
|
||||
/// This is the same as `FutureWarnFollowing`, except the last attribute is
|
||||
/// the one that is "used". Ideally these can eventually migrate to
|
||||
/// `ErrorPreceding`.
|
||||
FutureWarnPreceding,
|
||||
KeepLast,
|
||||
}
|
||||
|
||||
type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind;
|
||||
@@ -199,35 +216,35 @@ pub(crate) enum AttributeDuplicates {
|
||||
///
|
||||
/// [`CombineAttributeParser`] can only convert a single kind of attribute, and cannot combine multiple
|
||||
/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
|
||||
pub(crate) trait CombineAttributeParser: 'static {
|
||||
pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
|
||||
const PATH: &'static [Symbol];
|
||||
|
||||
type Item;
|
||||
const CONVERT: ConvertFn<Self::Item>;
|
||||
|
||||
/// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`]
|
||||
fn extend<'a>(
|
||||
cx: &'a AcceptContext<'a>,
|
||||
args: &'a ArgParser<'a>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'a;
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'c;
|
||||
}
|
||||
|
||||
pub(crate) struct Combine<T: CombineAttributeParser>(
|
||||
PhantomData<T>,
|
||||
ThinVec<<T as CombineAttributeParser>::Item>,
|
||||
pub(crate) struct Combine<T: CombineAttributeParser<S>, S: Stage>(
|
||||
PhantomData<(S, T)>,
|
||||
ThinVec<<T as CombineAttributeParser<S>>::Item>,
|
||||
);
|
||||
|
||||
impl<T: CombineAttributeParser> Default for Combine<T> {
|
||||
impl<T: CombineAttributeParser<S>, S: Stage> Default for Combine<T, S> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default(), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: CombineAttributeParser> AttributeParser for Combine<T> {
|
||||
const ATTRIBUTES: AcceptMapping<Self> =
|
||||
&[(T::PATH, |group: &mut Combine<T>, cx, args| group.1.extend(T::extend(cx, args)))];
|
||||
impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S> {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> =
|
||||
&[(T::PATH, |group: &mut Combine<T, S>, cx, args| group.1.extend(T::extend(cx, args)))];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
|
||||
|
||||
use super::{CombineAttributeParser, ConvertFn};
|
||||
use crate::context::AcceptContext;
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
|
||||
use crate::session_diagnostics;
|
||||
use crate::session_diagnostics::IncorrectReprFormatGenericCause;
|
||||
@@ -19,15 +19,15 @@
|
||||
// FIXME(jdonszelmann): is a vec the right representation here even? isn't it just a struct?
|
||||
pub(crate) struct ReprParser;
|
||||
|
||||
impl CombineAttributeParser for ReprParser {
|
||||
impl<S: Stage> CombineAttributeParser<S> for ReprParser {
|
||||
type Item = (ReprAttr, Span);
|
||||
const PATH: &'static [Symbol] = &[sym::repr];
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
|
||||
|
||||
fn extend<'a>(
|
||||
cx: &'a AcceptContext<'a>,
|
||||
args: &'a ArgParser<'a>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'a {
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
||||
let mut reprs = Vec::new();
|
||||
|
||||
let Some(list) = args.list() else {
|
||||
@@ -91,7 +91,10 @@ fn int_type_of_word(s: Symbol) -> Option<IntType> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option<ReprAttr> {
|
||||
fn parse_repr<S: Stage>(
|
||||
cx: &AcceptContext<'_, '_, S>,
|
||||
param: &MetaItemParser<'_>,
|
||||
) -> Option<ReprAttr> {
|
||||
use ReprAttr::*;
|
||||
|
||||
// FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
|
||||
@@ -180,8 +183,8 @@ enum AlignKind {
|
||||
Align,
|
||||
}
|
||||
|
||||
fn parse_repr_align(
|
||||
cx: &AcceptContext<'_>,
|
||||
fn parse_repr_align<S: Stage>(
|
||||
cx: &AcceptContext<'_, '_, S>,
|
||||
list: &MetaItemListParser<'_>,
|
||||
param_span: Span,
|
||||
align_kind: AlignKind,
|
||||
|
||||
@@ -8,10 +8,8 @@
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::util::parse_version;
|
||||
use super::{
|
||||
AcceptMapping, AttributeDuplicates, AttributeParser, OnDuplicate, SingleAttributeParser,
|
||||
};
|
||||
use crate::context::{AcceptContext, FinalizeContext};
|
||||
use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, FinalizeContext, Stage};
|
||||
use crate::parser::{ArgParser, MetaItemParser};
|
||||
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
|
||||
|
||||
@@ -33,7 +31,7 @@ pub(crate) struct StabilityParser {
|
||||
|
||||
impl StabilityParser {
|
||||
/// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate.
|
||||
fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
|
||||
fn check_duplicate<S: Stage>(&self, cx: &AcceptContext<'_, '_, S>) -> bool {
|
||||
if let Some((_, _)) = self.stability {
|
||||
cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
|
||||
true
|
||||
@@ -43,8 +41,8 @@ fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributeParser for StabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self> = &[
|
||||
impl<S: Stage> AttributeParser<S> for StabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[
|
||||
(&[sym::stable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
@@ -67,7 +65,7 @@ impl AttributeParser for StabilityParser {
|
||||
}),
|
||||
];
|
||||
|
||||
fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if let Some(atum) = self.allowed_through_unstable_modules {
|
||||
if let Some((
|
||||
Stability {
|
||||
@@ -97,8 +95,8 @@ pub(crate) struct BodyStabilityParser {
|
||||
stability: Option<(DefaultBodyStability, Span)>,
|
||||
}
|
||||
|
||||
impl AttributeParser for BodyStabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self> =
|
||||
impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> =
|
||||
&[(&[sym::rustc_default_body_unstable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if this.stability.is_some() {
|
||||
@@ -109,7 +107,7 @@ impl AttributeParser for BodyStabilityParser {
|
||||
}
|
||||
})];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
let (stability, span) = self.stability?;
|
||||
|
||||
Some(AttributeKind::BodyStability { stability, span })
|
||||
@@ -118,12 +116,12 @@ fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
|
||||
pub(crate) struct ConstStabilityIndirectParser;
|
||||
// FIXME(jdonszelmann): single word attribute group when we have these
|
||||
impl SingleAttributeParser for ConstStabilityIndirectParser {
|
||||
impl<S: Stage> SingleAttributeParser<S> for ConstStabilityIndirectParser {
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_const_stable_indirect];
|
||||
const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing;
|
||||
const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore;
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore;
|
||||
|
||||
fn convert(_cx: &AcceptContext<'_>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(_cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::ConstStabilityIndirect)
|
||||
}
|
||||
}
|
||||
@@ -136,7 +134,7 @@ pub(crate) struct ConstStabilityParser {
|
||||
|
||||
impl ConstStabilityParser {
|
||||
/// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate.
|
||||
fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
|
||||
fn check_duplicate<S: Stage>(&self, cx: &AcceptContext<'_, '_, S>) -> bool {
|
||||
if let Some((_, _)) = self.stability {
|
||||
cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
|
||||
true
|
||||
@@ -146,8 +144,8 @@ fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributeParser for ConstStabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self> = &[
|
||||
impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[
|
||||
(&[sym::rustc_const_stable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
|
||||
@@ -177,7 +175,7 @@ impl AttributeParser for ConstStabilityParser {
|
||||
}),
|
||||
];
|
||||
|
||||
fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if self.promotable {
|
||||
if let Some((ref mut stab, _)) = self.stability {
|
||||
stab.promotable = true;
|
||||
@@ -197,8 +195,8 @@ fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
///
|
||||
/// Emits an error when either the option was already Some, or the arguments weren't of form
|
||||
/// `name = value`
|
||||
fn insert_value_into_option_or_error(
|
||||
cx: &AcceptContext<'_>,
|
||||
fn insert_value_into_option_or_error<S: Stage>(
|
||||
cx: &AcceptContext<'_, '_, S>,
|
||||
param: &MetaItemParser<'_>,
|
||||
item: &mut Option<Symbol>,
|
||||
) -> Option<()> {
|
||||
@@ -224,8 +222,8 @@ fn insert_value_into_option_or_error(
|
||||
|
||||
/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
|
||||
/// its stability information.
|
||||
pub(crate) fn parse_stability(
|
||||
cx: &AcceptContext<'_>,
|
||||
pub(crate) fn parse_stability<S: Stage>(
|
||||
cx: &AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser<'_>,
|
||||
) -> Option<(Symbol, StabilityLevel)> {
|
||||
let mut feature = None;
|
||||
@@ -290,8 +288,8 @@ pub(crate) fn parse_stability(
|
||||
|
||||
// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
|
||||
/// attribute, and return the feature name and its stability information.
|
||||
pub(crate) fn parse_unstability(
|
||||
cx: &AcceptContext<'_>,
|
||||
pub(crate) fn parse_unstability<S: Stage>(
|
||||
cx: &AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser<'_>,
|
||||
) -> Option<(Symbol, StabilityLevel)> {
|
||||
let mut feature = None;
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use super::{AcceptContext, AttributeDuplicates, OnDuplicate, SingleAttributeParser};
|
||||
use super::{AcceptContext, AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::Stage;
|
||||
use crate::parser::ArgParser;
|
||||
|
||||
pub(crate) struct TransparencyParser;
|
||||
@@ -10,14 +11,14 @@
|
||||
// FIXME(jdonszelmann): make these proper diagnostics
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
impl SingleAttributeParser for TransparencyParser {
|
||||
impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_macro_transparency];
|
||||
const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing;
|
||||
const ON_DUPLICATE: OnDuplicate = OnDuplicate::Custom(|cx, used, unused| {
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Custom(|cx, used, unused| {
|
||||
cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes");
|
||||
});
|
||||
|
||||
fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
match args.name_value().and_then(|nv| nv.value_as_str()) {
|
||||
Some(sym::transparent) => Some(Transparency::Transparent),
|
||||
Some(sym::semiopaque | sym::semitransparent) => Some(Transparency::SemiOpaque),
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Deref;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use private::Sealed;
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::NodeId;
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
|
||||
use rustc_errors::{DiagCtxtHandle, Diagnostic};
|
||||
use rustc_feature::Features;
|
||||
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId};
|
||||
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
|
||||
|
||||
@@ -22,20 +26,40 @@
|
||||
use crate::attributes::{AttributeParser as _, Combine, Single};
|
||||
use crate::parser::{ArgParser, MetaItemParser};
|
||||
|
||||
macro_rules! group_type {
|
||||
($stage: ty) => {
|
||||
LazyLock<(
|
||||
BTreeMap<&'static [Symbol], Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>>,
|
||||
Vec<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $stage>) -> Option<AttributeKind>>>
|
||||
)>
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! attribute_parsers {
|
||||
(
|
||||
pub(crate) static $name: ident = [$($names: ty),* $(,)?];
|
||||
) => {
|
||||
type Accepts = BTreeMap<
|
||||
&'static [Symbol],
|
||||
Box<dyn Send + Sync + Fn(&AcceptContext<'_>, &ArgParser<'_>)>
|
||||
>;
|
||||
type Finalizes = Vec<
|
||||
Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>
|
||||
>;
|
||||
pub(crate) static $name: LazyLock<(Accepts, Finalizes)> = LazyLock::new(|| {
|
||||
let mut accepts = Accepts::new();
|
||||
let mut finalizes = Finalizes::new();
|
||||
mod early {
|
||||
use super::*;
|
||||
type Combine<T> = super::Combine<T, Early>;
|
||||
type Single<T> = super::Single<T, Early>;
|
||||
|
||||
attribute_parsers!(@[Early] pub(crate) static $name = [$($names),*];);
|
||||
}
|
||||
mod late {
|
||||
use super::*;
|
||||
type Combine<T> = super::Combine<T, Late>;
|
||||
type Single<T> = super::Single<T, Late>;
|
||||
|
||||
attribute_parsers!(@[Late] pub(crate) static $name = [$($names),*];);
|
||||
}
|
||||
};
|
||||
(
|
||||
@[$ty: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
|
||||
) => {
|
||||
pub(crate) static $name: group_type!($ty) = LazyLock::new(|| {
|
||||
let mut accepts = BTreeMap::<_, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>>::new();
|
||||
let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $ty>) -> Option<AttributeKind>>>::new();
|
||||
$(
|
||||
{
|
||||
thread_local! {
|
||||
@@ -62,7 +86,6 @@ macro_rules! attribute_parsers {
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
attribute_parsers!(
|
||||
pub(crate) static ATTRIBUTE_PARSERS = [
|
||||
// tidy-alphabetical-start
|
||||
@@ -86,50 +109,114 @@ macro_rules! attribute_parsers {
|
||||
];
|
||||
);
|
||||
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
impl Sealed for super::Early {}
|
||||
impl Sealed for super::Late {}
|
||||
}
|
||||
|
||||
// allow because it's a sealed trait
|
||||
#[allow(private_interfaces)]
|
||||
pub trait Stage: Sized + 'static + Sealed {
|
||||
type Id: Copy;
|
||||
|
||||
fn parsers() -> &'static group_type!(Self);
|
||||
|
||||
fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed;
|
||||
}
|
||||
|
||||
// allow because it's a sealed trait
|
||||
#[allow(private_interfaces)]
|
||||
impl Stage for Early {
|
||||
type Id = NodeId;
|
||||
|
||||
fn parsers() -> &'static group_type!(Self) {
|
||||
&early::ATTRIBUTE_PARSERS
|
||||
}
|
||||
fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
|
||||
sess.dcx().create_err(diag).delay_as_bug()
|
||||
}
|
||||
}
|
||||
|
||||
// allow because it's a sealed trait
|
||||
#[allow(private_interfaces)]
|
||||
impl Stage for Late {
|
||||
type Id = HirId;
|
||||
|
||||
fn parsers() -> &'static group_type!(Self) {
|
||||
&late::ATTRIBUTE_PARSERS
|
||||
}
|
||||
fn emit_err<'sess>(tcx: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
|
||||
tcx.dcx().emit_err(diag)
|
||||
}
|
||||
}
|
||||
|
||||
/// used when parsing attributes for miscelaneous things *before* ast lowering
|
||||
pub struct Early;
|
||||
/// used when parsing attributes during ast lowering
|
||||
pub struct Late;
|
||||
|
||||
/// Context given to every attribute parser when accepting
|
||||
///
|
||||
/// Gives [`AttributeParser`]s enough information to create errors, for example.
|
||||
pub(crate) struct AcceptContext<'a> {
|
||||
pub(crate) finalize_cx: &'a FinalizeContext<'a>,
|
||||
pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
|
||||
pub(crate) finalize_cx: FinalizeContext<'f, 'sess, S>,
|
||||
/// The span of the attribute currently being parsed
|
||||
pub(crate) attr_span: Span,
|
||||
}
|
||||
|
||||
impl<'a> AcceptContext<'a> {
|
||||
pub(crate) fn emit_err(&self, diag: impl Diagnostic<'a>) -> ErrorGuaranteed {
|
||||
if self.limit_diagnostics {
|
||||
self.dcx().create_err(diag).delay_as_bug()
|
||||
} else {
|
||||
self.dcx().emit_err(diag)
|
||||
}
|
||||
impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
||||
pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
|
||||
S::emit_err(&self.sess, diag)
|
||||
}
|
||||
|
||||
pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
|
||||
let id = self.target_id;
|
||||
(self.emit_lint)(AttributeLint { id, span, kind: lint });
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for AcceptContext<'a> {
|
||||
type Target = FinalizeContext<'a>;
|
||||
impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
|
||||
type Target = FinalizeContext<'f, 'sess, S>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.finalize_cx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.finalize_cx
|
||||
}
|
||||
}
|
||||
|
||||
/// Context given to every attribute parser during finalization.
|
||||
///
|
||||
/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create
|
||||
/// errors, for example.
|
||||
pub(crate) struct FinalizeContext<'a> {
|
||||
pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
|
||||
/// The parse context, gives access to the session and the
|
||||
/// diagnostics context.
|
||||
pub(crate) cx: &'a AttributeParser<'a>,
|
||||
pub(crate) cx: &'p mut AttributeParser<'sess, S>,
|
||||
/// The span of the syntactical component this attribute was applied to
|
||||
pub(crate) target_span: Span,
|
||||
/// The id ([`NodeId`] if `S` is `Early`, [`HirId`] if `S` is `Late`) of the syntactical component this attribute was applied to
|
||||
pub(crate) target_id: S::Id,
|
||||
|
||||
pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
|
||||
}
|
||||
|
||||
impl<'a> Deref for FinalizeContext<'a> {
|
||||
type Target = AttributeParser<'a>;
|
||||
impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> {
|
||||
type Target = AttributeParser<'sess, S>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.cx
|
||||
self.cx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.cx
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,23 +228,20 @@ pub enum OmitDoc {
|
||||
|
||||
/// Context created once, for example as part of the ast lowering
|
||||
/// context, through which all attributes can be lowered.
|
||||
pub struct AttributeParser<'sess> {
|
||||
pub struct AttributeParser<'sess, S: Stage = Late> {
|
||||
#[expect(dead_code)] // FIXME(jdonszelmann): needed later to verify we parsed all attributes
|
||||
tools: Vec<Symbol>,
|
||||
sess: &'sess Session,
|
||||
features: Option<&'sess Features>,
|
||||
sess: &'sess Session,
|
||||
stage: PhantomData<S>,
|
||||
|
||||
/// *Only* parse attributes with this symbol.
|
||||
///
|
||||
/// Used in cases where we want the lowering infrastructure for parse just a single attribute.
|
||||
parse_only: Option<Symbol>,
|
||||
|
||||
/// Can be used to instruct parsers to reduce the number of diagnostics it emits.
|
||||
/// Useful when using `parse_limited` and you know the attr will be reparsed later.
|
||||
pub(crate) limit_diagnostics: bool,
|
||||
}
|
||||
|
||||
impl<'sess> AttributeParser<'sess> {
|
||||
impl<'sess> AttributeParser<'sess, Early> {
|
||||
/// This method allows you to parse attributes *before* you have access to features or tools.
|
||||
/// One example where this is necessary, is to parse `feature` attributes themselves for
|
||||
/// example.
|
||||
@@ -168,33 +252,53 @@ impl<'sess> AttributeParser<'sess> {
|
||||
///
|
||||
/// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
|
||||
/// that symbol are picked out of the list of instructions and parsed. Those are returned.
|
||||
///
|
||||
/// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while
|
||||
/// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed
|
||||
/// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors
|
||||
pub fn parse_limited(
|
||||
sess: &'sess Session,
|
||||
attrs: &[ast::Attribute],
|
||||
sym: Symbol,
|
||||
target_span: Span,
|
||||
limit_diagnostics: bool,
|
||||
target_node_id: NodeId,
|
||||
) -> Option<Attribute> {
|
||||
let mut parsed = Self {
|
||||
sess,
|
||||
let mut p = Self {
|
||||
features: None,
|
||||
tools: Vec::new(),
|
||||
parse_only: Some(sym),
|
||||
limit_diagnostics,
|
||||
}
|
||||
.parse_attribute_list(attrs, target_span, OmitDoc::Skip, std::convert::identity);
|
||||
|
||||
sess,
|
||||
stage: PhantomData,
|
||||
};
|
||||
let mut parsed = p.parse_attribute_list(
|
||||
attrs,
|
||||
target_span,
|
||||
target_node_id,
|
||||
OmitDoc::Skip,
|
||||
std::convert::identity,
|
||||
|_lint| {
|
||||
panic!("can't emit lints here for now (nothing uses this atm)");
|
||||
},
|
||||
);
|
||||
assert!(parsed.len() <= 1);
|
||||
|
||||
parsed.pop()
|
||||
}
|
||||
|
||||
pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
|
||||
Self { sess, features: Some(features), tools, parse_only: None, limit_diagnostics: false }
|
||||
pub fn new_early(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
|
||||
Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'sess> AttributeParser<'sess, Late> {
|
||||
pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
|
||||
Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
||||
pub(crate) fn sess(&self) -> &'sess Session {
|
||||
self.sess
|
||||
&self.sess
|
||||
}
|
||||
|
||||
pub(crate) fn features(&self) -> &'sess Features {
|
||||
@@ -202,25 +306,25 @@ pub(crate) fn features(&self) -> &'sess Features {
|
||||
}
|
||||
|
||||
pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
|
||||
self.sess.dcx()
|
||||
self.sess().dcx()
|
||||
}
|
||||
|
||||
/// Parse a list of attributes.
|
||||
///
|
||||
/// `target_span` is the span of the thing this list of attributes is applied to,
|
||||
/// and when `omit_doc` is set, doc attributes are filtered out.
|
||||
pub fn parse_attribute_list<'a>(
|
||||
&'a self,
|
||||
attrs: &'a [ast::Attribute],
|
||||
pub fn parse_attribute_list(
|
||||
&mut self,
|
||||
attrs: &[ast::Attribute],
|
||||
target_span: Span,
|
||||
target_id: S::Id,
|
||||
omit_doc: OmitDoc,
|
||||
|
||||
lower_span: impl Copy + Fn(Span) -> Span,
|
||||
mut emit_lint: impl FnMut(AttributeLint<S::Id>),
|
||||
) -> Vec<Attribute> {
|
||||
let mut attributes = Vec::new();
|
||||
|
||||
let finalize_cx = FinalizeContext { cx: self, target_span };
|
||||
|
||||
for attr in attrs {
|
||||
// If we're only looking for a single attribute, skip all the ones we don't care about.
|
||||
if let Some(expected) = self.parse_only {
|
||||
@@ -268,13 +372,18 @@ pub fn parse_attribute_list<'a>(
|
||||
let args = parser.args();
|
||||
let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
|
||||
|
||||
if let Some(accept) = ATTRIBUTE_PARSERS.0.get(parts.as_slice()) {
|
||||
let cx = AcceptContext {
|
||||
finalize_cx: &finalize_cx,
|
||||
if let Some(accept) = S::parsers().0.get(parts.as_slice()) {
|
||||
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
|
||||
finalize_cx: FinalizeContext {
|
||||
cx: self,
|
||||
target_span,
|
||||
target_id,
|
||||
emit_lint: &mut emit_lint,
|
||||
},
|
||||
attr_span: lower_span(attr.span),
|
||||
};
|
||||
|
||||
accept(&cx, &args)
|
||||
accept(&mut cx, args)
|
||||
} else {
|
||||
// If we're here, we must be compiling a tool attribute... Or someone
|
||||
// forgot to parse their fancy new attribute. Let's warn them in any case.
|
||||
@@ -304,8 +413,13 @@ pub fn parse_attribute_list<'a>(
|
||||
}
|
||||
|
||||
let mut parsed_attributes = Vec::new();
|
||||
for f in &ATTRIBUTE_PARSERS.1 {
|
||||
if let Some(attr) = f(&finalize_cx) {
|
||||
for f in &S::parsers().1 {
|
||||
if let Some(attr) = f(&mut FinalizeContext {
|
||||
cx: self,
|
||||
target_span,
|
||||
target_id,
|
||||
emit_lint: &mut emit_lint,
|
||||
}) {
|
||||
parsed_attributes.push(Attribute::Parsed(attr));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
//! a "stability" of an item. So, the stability attribute has an
|
||||
//! [`AttributeParser`](attributes::AttributeParser) that recognizes both the `#[stable()]`
|
||||
//! and `#[unstable()]` syntactic attributes, and at the end produce a single
|
||||
//! [`AttributeKind::Stability`](rustc_attr_data_structures::AttributeKind::Stability).
|
||||
//! [`AttributeKind::Stability`].
|
||||
//!
|
||||
//! When multiple instances of the same attribute are allowed, they're combined into a single
|
||||
//! semantic attribute. For example:
|
||||
@@ -86,6 +86,7 @@
|
||||
#[macro_use]
|
||||
mod attributes;
|
||||
mod context;
|
||||
mod lints;
|
||||
pub mod parser;
|
||||
mod session_diagnostics;
|
||||
|
||||
@@ -93,6 +94,7 @@
|
||||
pub use attributes::util::{
|
||||
find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
|
||||
};
|
||||
pub use context::{AttributeParser, OmitDoc};
|
||||
pub use context::{AttributeParser, Early, Late, OmitDoc};
|
||||
pub use lints::emit_attribute_lint;
|
||||
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
|
||||
use rustc_errors::LintEmitter;
|
||||
use rustc_hir::HirId;
|
||||
|
||||
use crate::session_diagnostics;
|
||||
|
||||
pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emitter: L) {
|
||||
let AttributeLint { id, span, kind } = lint;
|
||||
|
||||
match kind {
|
||||
&AttributeLintKind::UnusedDuplicate { this, other, warning } => lint_emitter
|
||||
.emit_node_span_lint(
|
||||
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
|
||||
*id,
|
||||
*span,
|
||||
session_diagnostics::UnusedDuplicate { this, other, warning },
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@ pub fn span(&self) -> Option<Span> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_attr_args(value: &'a AttrArgs, dcx: DiagCtxtHandle<'a>) -> Self {
|
||||
pub fn from_attr_args<'sess>(value: &'a AttrArgs, dcx: DiagCtxtHandle<'sess>) -> Self {
|
||||
match value {
|
||||
AttrArgs::Empty => Self::NoArgs,
|
||||
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
|
||||
@@ -235,7 +235,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
impl<'a> MetaItemParser<'a> {
|
||||
/// Create a new parser from a [`NormalAttr`], which is stored inside of any
|
||||
/// [`ast::Attribute`](rustc_ast::Attribute)
|
||||
pub fn from_attr(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'a>) -> Self {
|
||||
pub fn from_attr<'sess>(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'sess>) -> Self {
|
||||
Self {
|
||||
path: PathParser::Ast(&attr.item.path),
|
||||
args: ArgParser::from_attr_args(&attr.item.args, dcx),
|
||||
@@ -320,13 +320,13 @@ fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit
|
||||
}
|
||||
}
|
||||
|
||||
struct MetaItemListParserContext<'a> {
|
||||
struct MetaItemListParserContext<'a, 'sess> {
|
||||
// the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside
|
||||
inside_delimiters: Peekable<TokenStreamIter<'a>>,
|
||||
dcx: DiagCtxtHandle<'a>,
|
||||
dcx: DiagCtxtHandle<'sess>,
|
||||
}
|
||||
|
||||
impl<'a> MetaItemListParserContext<'a> {
|
||||
impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
|
||||
fn done(&mut self) -> bool {
|
||||
self.inside_delimiters.peek().is_none()
|
||||
}
|
||||
@@ -507,11 +507,11 @@ pub struct MetaItemListParser<'a> {
|
||||
}
|
||||
|
||||
impl<'a> MetaItemListParser<'a> {
|
||||
fn new(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'a>) -> MetaItemListParser<'a> {
|
||||
fn new<'sess>(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'sess>) -> Self {
|
||||
MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx)
|
||||
}
|
||||
|
||||
fn new_tts(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'a>) -> Self {
|
||||
fn new_tts<'sess>(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'sess>) -> Self {
|
||||
MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use rustc_ast as ast;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
|
||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
use crate::fluent_generated as fluent;
|
||||
@@ -451,6 +451,17 @@ pub(crate) struct UnusedMultiple {
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_unused_multiple)]
|
||||
pub(crate) struct UnusedDuplicate {
|
||||
#[suggestion(code = "", applicability = "machine-applicable")]
|
||||
pub this: Span,
|
||||
#[note]
|
||||
pub other: Span,
|
||||
#[warning]
|
||||
pub warning: bool,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_stability_outside_std, code = E0734)]
|
||||
pub(crate) struct StabilityOutsideStd {
|
||||
|
||||
@@ -484,7 +484,7 @@ pub(crate) fn expand_ext(
|
||||
match item {
|
||||
Annotatable::Item(item) => {
|
||||
let is_packed = matches!(
|
||||
AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, true),
|
||||
AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, item.id),
|
||||
Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(x, _)| matches!(x, ReprPacked(..)))
|
||||
);
|
||||
|
||||
|
||||
@@ -60,8 +60,9 @@
|
||||
SubdiagMessage, fallback_fluent_bundle, fluent_bundle,
|
||||
};
|
||||
use rustc_hashes::Hash128;
|
||||
use rustc_lint_defs::LintExpectationId;
|
||||
use rustc_hir::HirId;
|
||||
pub use rustc_lint_defs::{Applicability, listify, pluralize};
|
||||
use rustc_lint_defs::{Lint, LintExpectationId};
|
||||
use rustc_macros::{Decodable, Encodable};
|
||||
pub use rustc_span::ErrorGuaranteed;
|
||||
pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
|
||||
@@ -101,6 +102,19 @@
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24);
|
||||
|
||||
/// Used to avoid depending on `rustc_middle` in `rustc_attr_parsing`.
|
||||
/// Always the `TyCtxt`.
|
||||
pub trait LintEmitter: Copy {
|
||||
#[track_caller]
|
||||
fn emit_node_span_lint(
|
||||
self,
|
||||
lint: &'static Lint,
|
||||
hir_id: HirId,
|
||||
span: impl Into<MultiSpan>,
|
||||
decorator: impl for<'a> LintDiagnostic<'a, ()>,
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)]
|
||||
pub enum SuggestionStyle {
|
||||
/// Hide the suggested code when displaying this suggestion inline.
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
use crate::def_id::{DefId, LocalDefIdMap};
|
||||
pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId};
|
||||
use crate::intravisit::{FnKind, VisitorExt};
|
||||
use crate::lints::DelayedLints;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)]
|
||||
pub enum AngleBrackets {
|
||||
@@ -1526,6 +1527,10 @@ pub struct OwnerInfo<'hir> {
|
||||
/// Map indicating what traits are in scope for places where this
|
||||
/// is relevant; generated by resolve.
|
||||
pub trait_map: ItemLocalMap<Box<[TraitCandidate]>>,
|
||||
|
||||
/// Lints delayed during ast lowering to be emitted
|
||||
/// after hir has completely built
|
||||
pub delayed_lints: DelayedLints,
|
||||
}
|
||||
|
||||
impl<'tcx> OwnerInfo<'tcx> {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
pub mod hir_id;
|
||||
pub mod intravisit;
|
||||
pub mod lang_items;
|
||||
pub mod lints;
|
||||
pub mod pat_util;
|
||||
mod stable_hash_impls;
|
||||
mod target;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
use rustc_attr_data_structures::lints::AttributeLint;
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_macros::HashStable_Generic;
|
||||
|
||||
use crate::HirId;
|
||||
|
||||
/// During ast lowering, no lints can be emitted.
|
||||
/// That is because lints attach to nodes either in the AST, or on the built HIR.
|
||||
/// When attached to AST nodes, they're emitted just before building HIR,
|
||||
/// and then there's a gap where no lints can be emitted until HIR is done.
|
||||
/// The variants in this enum represent lints that are temporarily stashed during
|
||||
/// AST lowering to be emitted once HIR is built.
|
||||
#[derive(Clone, Debug, HashStable_Generic)]
|
||||
pub enum DelayedLint {
|
||||
AttributeParsing(AttributeLint<HirId>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DelayedLints {
|
||||
pub lints: Box<[DelayedLint]>,
|
||||
// Only present when the crate hash is needed.
|
||||
pub opt_hash: Option<Fingerprint>,
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId,
|
||||
};
|
||||
use crate::hir_id::{HirId, ItemLocalId};
|
||||
use crate::lints::DelayedLints;
|
||||
|
||||
/// Requirements for a `StableHashingContext` to be used in this crate.
|
||||
/// This is a hack to allow using the `HashStable_Generic` derive macro
|
||||
@@ -102,6 +103,13 @@ fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for DelayedLints {
|
||||
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
|
||||
let DelayedLints { opt_hash, .. } = *self;
|
||||
opt_hash.unwrap().hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for AttributeMap<'tcx> {
|
||||
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
|
||||
// We ignore the `map` since it refers to information included in `opt_hash` which is
|
||||
|
||||
@@ -14,6 +14,7 @@ rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
|
||||
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
|
||||
@@ -92,8 +92,9 @@
|
||||
|
||||
pub use errors::NoVariantNamed;
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::lints::DelayedLint;
|
||||
use rustc_hir::{self as hir};
|
||||
use rustc_middle::middle;
|
||||
use rustc_middle::mir::interpret::GlobalId;
|
||||
use rustc_middle::query::Providers;
|
||||
@@ -174,6 +175,14 @@ pub fn provide(providers: &mut Providers) {
|
||||
};
|
||||
}
|
||||
|
||||
fn emit_delayed_lint(lint: &DelayedLint, tcx: TyCtxt<'_>) {
|
||||
match lint {
|
||||
DelayedLint::AttributeParsing(attribute_lint) => {
|
||||
rustc_attr_parsing::emit_attribute_lint(attribute_lint, tcx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_crate(tcx: TyCtxt<'_>) {
|
||||
let _prof_timer = tcx.sess.timer("type_check_crate");
|
||||
|
||||
@@ -192,6 +201,14 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
|
||||
let _: R = tcx.ensure_ok().crate_inherent_impls_overlap_check(());
|
||||
});
|
||||
|
||||
for owner_id in tcx.hir_crate_items(()).owners() {
|
||||
if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) {
|
||||
for lint in &delayed_lints.lints {
|
||||
emit_delayed_lint(lint, tcx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tcx.par_hir_body_owners(|item_def_id| {
|
||||
let def_kind = tcx.def_kind(item_def_id);
|
||||
// Make sure we evaluate all static and (non-associated) const items, even if unused.
|
||||
|
||||
@@ -164,7 +164,7 @@ fn check_case(&self, cx: &EarlyContext<'_>, sort: &str, ident: &Ident) {
|
||||
impl EarlyLintPass for NonCamelCaseTypes {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
|
||||
let has_repr_c = matches!(
|
||||
AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, true),
|
||||
AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, it.id),
|
||||
Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC)
|
||||
);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
use rustc_data_structures::sync::{DynSend, DynSync, try_par_for_each_in};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
|
||||
use rustc_hir::lints::DelayedLint;
|
||||
use rustc_hir::*;
|
||||
use rustc_macros::{Decodable, Encodable, HashStable};
|
||||
use rustc_span::{ErrorGuaranteed, ExpnId, Span};
|
||||
@@ -161,8 +162,9 @@ pub fn hash_owner_nodes(
|
||||
node: OwnerNode<'_>,
|
||||
bodies: &SortedMap<ItemLocalId, &Body<'_>>,
|
||||
attrs: &SortedMap<ItemLocalId, &[Attribute]>,
|
||||
delayed_lints: &[DelayedLint],
|
||||
define_opaque: Option<&[(Span, LocalDefId)]>,
|
||||
) -> (Option<Fingerprint>, Option<Fingerprint>) {
|
||||
) -> (Option<Fingerprint>, Option<Fingerprint>, Option<Fingerprint>) {
|
||||
if self.needs_crate_hash() {
|
||||
self.with_stable_hashing_context(|mut hcx| {
|
||||
let mut stable_hasher = StableHasher::new();
|
||||
@@ -178,10 +180,16 @@ pub fn hash_owner_nodes(
|
||||
define_opaque.hash_stable(&mut hcx, &mut stable_hasher);
|
||||
|
||||
let h2 = stable_hasher.finish();
|
||||
(Some(h1), Some(h2))
|
||||
|
||||
// hash lints emitted during ast lowering
|
||||
let mut stable_hasher = StableHasher::new();
|
||||
delayed_lints.hash_stable(&mut hcx, &mut stable_hasher);
|
||||
let h3 = stable_hasher.finish();
|
||||
|
||||
(Some(h1), Some(h2), Some(h3))
|
||||
})
|
||||
} else {
|
||||
(None, None)
|
||||
(None, None, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,6 +222,8 @@ pub fn provide(providers: &mut Providers) {
|
||||
providers.hir_attr_map = |tcx, id| {
|
||||
tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs)
|
||||
};
|
||||
providers.opt_ast_lowering_delayed_lints =
|
||||
|tcx, id| tcx.hir_crate(()).owners[id.def_id].as_owner().map(|o| &o.delayed_lints);
|
||||
providers.def_span = |tcx, def_id| tcx.hir_span(tcx.local_def_id_to_hir_id(def_id));
|
||||
providers.def_ident_span = |tcx, def_id| {
|
||||
let hir_id = tcx.local_def_id_to_hir_id(def_id);
|
||||
|
||||
@@ -221,6 +221,14 @@
|
||||
feedable
|
||||
}
|
||||
|
||||
/// Gives access to lints emitted during ast lowering.
|
||||
///
|
||||
/// This can be conveniently accessed by `tcx.hir_*` methods.
|
||||
/// Avoid calling this query directly.
|
||||
query opt_ast_lowering_delayed_lints(key: hir::OwnerId) -> Option<&'tcx hir::lints::DelayedLints> {
|
||||
desc { |tcx| "getting AST lowering delayed lints in `{}`", tcx.def_path_str(key) }
|
||||
}
|
||||
|
||||
/// Returns the *default* of the const pararameter given by `DefId`.
|
||||
///
|
||||
/// E.g., given `struct Ty<const N: usize = 3>;` this returns `3` for `N`.
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal,
|
||||
};
|
||||
use rustc_errors::{
|
||||
Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan,
|
||||
Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, LintEmitter, MultiSpan,
|
||||
};
|
||||
use rustc_hir::def::{CtorKind, DefKind};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
|
||||
@@ -1350,8 +1350,8 @@ pub fn feed_hir(&self) {
|
||||
let bodies = Default::default();
|
||||
let attrs = hir::AttributeMap::EMPTY;
|
||||
|
||||
let (opt_hash_including_bodies, _) =
|
||||
self.tcx.hash_owner_nodes(node, &bodies, &attrs.map, attrs.define_opaque);
|
||||
let (opt_hash_including_bodies, _, _) =
|
||||
self.tcx.hash_owner_nodes(node, &bodies, &attrs.map, &[], attrs.define_opaque);
|
||||
let node = node.into();
|
||||
self.opt_hir_owner_nodes(Some(self.tcx.arena.alloc(hir::OwnerNodes {
|
||||
opt_hash_including_bodies,
|
||||
@@ -1389,6 +1389,18 @@ pub struct TyCtxt<'tcx> {
|
||||
gcx: &'tcx GlobalCtxt<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> LintEmitter for TyCtxt<'tcx> {
|
||||
fn emit_node_span_lint(
|
||||
self,
|
||||
lint: &'static Lint,
|
||||
hir_id: HirId,
|
||||
span: impl Into<MultiSpan>,
|
||||
decorator: impl for<'a> LintDiagnostic<'a, ()>,
|
||||
) {
|
||||
self.emit_node_span_lint(lint, hir_id, span, decorator);
|
||||
}
|
||||
}
|
||||
|
||||
// Explicitly implement `DynSync` and `DynSend` for `TyCtxt` to short circuit trait resolution. Its
|
||||
// field are asserted to implement these traits below, so this is trivially safe, and it greatly
|
||||
// speeds-up compilation of this crate and its dependents.
|
||||
|
||||
@@ -128,7 +128,7 @@ fn visit_item(&mut self, i: &'a Item) {
|
||||
// FIXME(jdonszelmann) make one of these in the resolver?
|
||||
// FIXME(jdonszelmann) don't care about tools here maybe? Just parse what we can.
|
||||
// Does that prevents errors from happening? maybe
|
||||
let parser = AttributeParser::new(
|
||||
let mut parser = AttributeParser::new_early(
|
||||
&self.resolver.tcx.sess,
|
||||
self.resolver.tcx.features(),
|
||||
Vec::new(),
|
||||
@@ -136,8 +136,14 @@ fn visit_item(&mut self, i: &'a Item) {
|
||||
let attrs = parser.parse_attribute_list(
|
||||
&i.attrs,
|
||||
i.span,
|
||||
i.id,
|
||||
OmitDoc::Skip,
|
||||
std::convert::identity,
|
||||
|_l| {
|
||||
// FIXME(jdonszelmann): emit lints here properly
|
||||
// NOTE that before new attribute parsing, they didn't happen either
|
||||
// but it would be nice if we could change that.
|
||||
},
|
||||
);
|
||||
|
||||
let macro_data =
|
||||
|
||||
Reference in New Issue
Block a user