Skip to content

Commit 573a015

Browse files
committed
Auto merge of #138164 - jdonszelmann:attr-parsing-lint-infra, r=oli-obk
Infrastructure for lints during attribute parsing, specifically duplicate usages of attributes r? `@oli-obk` This PR adds a new field to OwnerInfo to buffer lints which are generated during attribute parsing and ast lowering in general. They can't be emitted at this stage because at that point there's no HIR yet, and early lints are already emitted. This also adds the generic `S: Stage` to attribute parsers. Currently we don't emit any lints during early attribute parsing, but if we ever want to that logic will be different. That's because there we don't have hir ids yet, while at the same time still having access to node ids and early lints. Even though that logic isn't completely there in this PR (no worries, we don't use it), that's why the parameter is there. With this PR, we also add 2 associated consts to `SingleAttributeParser`. Those determine what logic should be applied when finding a duplicate attribute. This PR was getting pretty large, so the first code using this logic is in #138165. This code is all new things that weren't possible before so it also doesn't break any behaviour. However, some of it will be dead code right now. I recommend reviewing both before merging, though in some sense that doubles the size of the review again, and the other PR might be more controversial. Let me know how you want to do this `@oli-obk`
2 parents 6c8138d + e2afe04 commit 573a015

File tree

32 files changed

+726
-226
lines changed

32 files changed

+726
-226
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3793,6 +3793,7 @@ dependencies = [
37933793
"rustc_arena",
37943794
"rustc_ast",
37953795
"rustc_attr_data_structures",
3796+
"rustc_attr_parsing",
37963797
"rustc_data_structures",
37973798
"rustc_errors",
37983799
"rustc_feature",

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
7373
// Merge attributes into the inner expression.
7474
if !e.attrs.is_empty() {
7575
let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]);
76-
let attrs = &*self.arena.alloc_from_iter(
77-
self.lower_attrs_vec(&e.attrs, e.span)
78-
.into_iter()
79-
.chain(old_attrs.iter().cloned()),
80-
);
81-
if attrs.is_empty() {
76+
let new_attrs = self
77+
.lower_attrs_vec(&e.attrs, e.span, ex.hir_id)
78+
.into_iter()
79+
.chain(old_attrs.iter().cloned());
80+
let new_attrs = &*self.arena.alloc_from_iter(new_attrs);
81+
if new_attrs.is_empty() {
8282
return ex;
8383
}
84-
85-
self.attrs.insert(ex.hir_id.local_id, attrs);
84+
self.attrs.insert(ex.hir_id.local_id, new_attrs);
8685
}
8786
return ex;
8887
}
@@ -2035,7 +2034,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
20352034
let ret_expr = self.checked_return(Some(from_residual_expr));
20362035
self.arena.alloc(self.expr(try_span, ret_expr))
20372036
};
2038-
self.lower_attrs(ret_expr.hir_id, &attrs, ret_expr.span);
2037+
self.lower_attrs(ret_expr.hir_id, &attrs, span);
20392038

20402039
let break_pat = self.pat_cf_break(try_span, residual_local);
20412040
self.arm(break_pat, ret_expr)

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ use rustc_data_structures::tagged_ptr::TaggedRef;
5151
use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
5252
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
5353
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
54+
use rustc_hir::lints::DelayedLint;
5455
use rustc_hir::{
5556
self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem,
5657
LifetimeSource, LifetimeSyntax, ParamName, TraitCandidate,
@@ -141,6 +142,8 @@ struct LoweringContext<'a, 'hir> {
141142
allow_for_await: Arc<[Symbol]>,
142143
allow_async_fn_traits: Arc<[Symbol]>,
143144

145+
delayed_lints: Vec<DelayedLint>,
146+
144147
attribute_parser: AttributeParser<'hir>,
145148
}
146149

@@ -190,6 +193,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
190193
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
191194

192195
attribute_parser: AttributeParser::new(tcx.sess, tcx.features(), registered_tools),
196+
delayed_lints: Vec::new(),
193197
}
194198
}
195199

@@ -198,6 +202,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
198202
}
199203
}
200204

205+
struct SpanLowerer {
206+
is_incremental: bool,
207+
def_id: LocalDefId,
208+
}
209+
210+
impl SpanLowerer {
211+
fn lower(&self, span: Span) -> Span {
212+
if self.is_incremental {
213+
span.with_parent(Some(self.def_id))
214+
} else {
215+
// Do not make spans relative when not using incremental compilation.
216+
span
217+
}
218+
}
219+
}
220+
201221
#[extension(trait ResolverAstLoweringExt)]
202222
impl ResolverAstLowering {
203223
fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>> {
@@ -573,6 +593,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
573593
std::mem::replace(&mut self.item_local_id_counter, hir::ItemLocalId::new(1));
574594
let current_impl_trait_defs = std::mem::take(&mut self.impl_trait_defs);
575595
let current_impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds);
596+
let current_delayed_lints = std::mem::take(&mut self.delayed_lints);
576597

577598
// Do not reset `next_node_id` and `node_id_to_def_id`:
578599
// we want `f` to be able to refer to the `LocalDefId`s that the caller created.
@@ -606,6 +627,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
606627
self.item_local_id_counter = current_local_counter;
607628
self.impl_trait_defs = current_impl_trait_defs;
608629
self.impl_trait_bounds = current_impl_trait_bounds;
630+
self.delayed_lints = current_delayed_lints;
609631

610632
debug_assert!(!self.children.iter().any(|(id, _)| id == &owner_id.def_id));
611633
self.children.push((owner_id.def_id, hir::MaybeOwner::Owner(info)));
@@ -616,6 +638,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
616638
let mut bodies = std::mem::take(&mut self.bodies);
617639
let define_opaque = std::mem::take(&mut self.define_opaque);
618640
let trait_map = std::mem::take(&mut self.trait_map);
641+
let delayed_lints = std::mem::take(&mut self.delayed_lints).into_boxed_slice();
619642

620643
#[cfg(debug_assertions)]
621644
for (id, attrs) in attrs.iter() {
@@ -629,14 +652,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
629652
let bodies = SortedMap::from_presorted_elements(bodies);
630653

631654
// Don't hash unless necessary, because it's expensive.
632-
let (opt_hash_including_bodies, attrs_hash) =
633-
self.tcx.hash_owner_nodes(node, &bodies, &attrs, define_opaque);
655+
let (opt_hash_including_bodies, attrs_hash, delayed_lints_hash) =
656+
self.tcx.hash_owner_nodes(node, &bodies, &attrs, &delayed_lints, define_opaque);
634657
let num_nodes = self.item_local_id_counter.as_usize();
635658
let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes);
636659
let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies };
637660
let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash, define_opaque };
661+
let delayed_lints =
662+
hir::lints::DelayedLints { lints: delayed_lints, opt_hash: delayed_lints_hash };
638663

639-
self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
664+
self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map, delayed_lints })
640665
}
641666

642667
/// This method allocates a new `HirId` for the given `NodeId`.
@@ -759,15 +784,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
759784
})
760785
}
761786

787+
fn span_lowerer(&self) -> SpanLowerer {
788+
SpanLowerer {
789+
is_incremental: self.tcx.sess.opts.incremental.is_some(),
790+
def_id: self.current_hir_id_owner.def_id,
791+
}
792+
}
793+
762794
/// Intercept all spans entering HIR.
763795
/// Mark a span as relative to the current owning item.
764796
fn lower_span(&self, span: Span) -> Span {
765-
if self.tcx.sess.opts.incremental.is_some() {
766-
span.with_parent(Some(self.current_hir_id_owner.def_id))
767-
} else {
768-
// Do not make spans relative when not using incremental compilation.
769-
span
770-
}
797+
self.span_lowerer().lower(span)
771798
}
772799

773800
fn lower_ident(&self, ident: Ident) -> Ident {
@@ -889,7 +916,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
889916
if attrs.is_empty() {
890917
&[]
891918
} else {
892-
let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span));
919+
let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span), id);
893920

894921
debug_assert_eq!(id.owner, self.current_hir_id_owner);
895922
let ret = self.arena.alloc_from_iter(lowered_attrs);
@@ -909,9 +936,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
909936
}
910937
}
911938

912-
fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec<hir::Attribute> {
913-
self.attribute_parser
914-
.parse_attribute_list(attrs, target_span, OmitDoc::Lower, |s| self.lower_span(s))
939+
fn lower_attrs_vec(
940+
&mut self,
941+
attrs: &[Attribute],
942+
target_span: Span,
943+
target_hir_id: HirId,
944+
) -> Vec<hir::Attribute> {
945+
let l = self.span_lowerer();
946+
self.attribute_parser.parse_attribute_list(
947+
attrs,
948+
target_span,
949+
target_hir_id,
950+
OmitDoc::Lower,
951+
|s| l.lower(s),
952+
|l| {
953+
self.delayed_lints.push(DelayedLint::AttributeParsing(l));
954+
},
955+
)
915956
}
916957

917958
fn alias_attrs(&mut self, id: HirId, target_id: HirId) {

compiler/rustc_attr_data_structures/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ mod attributes;
88
mod stability;
99
mod version;
1010

11+
pub mod lints;
12+
1113
use std::num::NonZero;
1214

1315
pub use attributes::*;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use rustc_macros::HashStable_Generic;
2+
use rustc_span::Span;
3+
4+
#[derive(Clone, Debug, HashStable_Generic)]
5+
pub struct AttributeLint<Id> {
6+
pub id: Id,
7+
pub span: Span,
8+
pub kind: AttributeLintKind,
9+
}
10+
11+
#[derive(Clone, Debug, HashStable_Generic)]
12+
pub enum AttributeLintKind {
13+
UnusedDuplicate { this: Span, other: Span, warning: bool },
14+
}

compiler/rustc_attr_parsing/messages.ftl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,15 @@ attr_parsing_unsupported_literal_generic =
131131
attr_parsing_unsupported_literal_suggestion =
132132
consider removing the prefix
133133
134+
attr_parsing_unused_duplicate =
135+
unused attribute
136+
.suggestion = remove this attribute
137+
.note = attribute also specified here
138+
.warn = {-passes_previously_accepted}
134139
attr_parsing_unused_multiple =
135140
multiple `{$name}` attributes
136141
.suggestion = remove this attribute
137142
.note = attribute also specified here
143+
144+
-attr_parsing_perviously_accepted =
145+
this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!

compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,43 @@ use rustc_attr_data_structures::AttributeKind;
44
use rustc_span::{Span, Symbol, sym};
55

66
use super::{CombineAttributeParser, ConvertFn};
7-
use crate::context::AcceptContext;
7+
use crate::context::{AcceptContext, Stage};
88
use crate::parser::ArgParser;
99
use crate::session_diagnostics;
1010

1111
pub(crate) struct AllowInternalUnstableParser;
12-
impl CombineAttributeParser for AllowInternalUnstableParser {
13-
const PATH: &'static [Symbol] = &[sym::allow_internal_unstable];
12+
impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
13+
const PATH: &[Symbol] = &[sym::allow_internal_unstable];
1414
type Item = (Symbol, Span);
1515
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
1616

17-
fn extend<'a>(
18-
cx: &'a AcceptContext<'a>,
19-
args: &'a ArgParser<'a>,
20-
) -> impl IntoIterator<Item = Self::Item> + 'a {
21-
parse_unstable(cx, args, Self::PATH[0]).into_iter().zip(iter::repeat(cx.attr_span))
17+
fn extend<'c>(
18+
cx: &'c mut AcceptContext<'_, '_, S>,
19+
args: &'c ArgParser<'_>,
20+
) -> impl IntoIterator<Item = Self::Item> {
21+
parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
22+
.into_iter()
23+
.zip(iter::repeat(cx.attr_span))
2224
}
2325
}
2426

2527
pub(crate) struct AllowConstFnUnstableParser;
26-
impl CombineAttributeParser for AllowConstFnUnstableParser {
27-
const PATH: &'static [Symbol] = &[sym::rustc_allow_const_fn_unstable];
28+
impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
29+
const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
2830
type Item = Symbol;
2931
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
3032

31-
fn extend<'a>(
32-
cx: &'a AcceptContext<'a>,
33-
args: &'a ArgParser<'a>,
34-
) -> impl IntoIterator<Item = Self::Item> + 'a {
35-
parse_unstable(cx, args, Self::PATH[0])
33+
fn extend<'c>(
34+
cx: &'c mut AcceptContext<'_, '_, S>,
35+
args: &'c ArgParser<'_>,
36+
) -> impl IntoIterator<Item = Self::Item> + 'c {
37+
parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
3638
}
3739
}
3840

39-
fn parse_unstable<'a>(
40-
cx: &AcceptContext<'_>,
41-
args: &'a ArgParser<'a>,
41+
fn parse_unstable<S: Stage>(
42+
cx: &AcceptContext<'_, '_, S>,
43+
args: &ArgParser<'_>,
4244
symbol: Symbol,
4345
) -> impl IntoIterator<Item = Symbol> {
4446
let mut res = Vec::new();

compiler/rustc_attr_parsing/src/attributes/confusables.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rustc_span::{Span, Symbol, sym};
33
use thin_vec::ThinVec;
44

55
use super::{AcceptMapping, AttributeParser};
6-
use crate::context::FinalizeContext;
6+
use crate::context::{FinalizeContext, Stage};
77
use crate::session_diagnostics;
88

99
#[derive(Default)]
@@ -12,8 +12,8 @@ pub(crate) struct ConfusablesParser {
1212
first_span: Option<Span>,
1313
}
1414

15-
impl AttributeParser for ConfusablesParser {
16-
const ATTRIBUTES: AcceptMapping<Self> = &[(&[sym::rustc_confusables], |this, cx, args| {
15+
impl<S: Stage> AttributeParser<S> for ConfusablesParser {
16+
const ATTRIBUTES: AcceptMapping<Self, S> = &[(&[sym::rustc_confusables], |this, cx, args| {
1717
let Some(list) = args.list() else {
1818
// FIXME(jdonszelmann): error when not a list? Bring validation code here.
1919
// NOTE: currently subsequent attributes are silently ignored using
@@ -45,7 +45,7 @@ impl AttributeParser for ConfusablesParser {
4545
this.first_span.get_or_insert(cx.attr_span);
4646
})];
4747

48-
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
48+
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
4949
if self.confusables.is_empty() {
5050
return None;
5151
}

compiler/rustc_attr_parsing/src/attributes/deprecation.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
22
use rustc_span::{Span, Symbol, sym};
33

4-
use super::SingleAttributeParser;
54
use super::util::parse_version;
6-
use crate::context::AcceptContext;
5+
use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
6+
use crate::context::{AcceptContext, Stage};
77
use crate::parser::ArgParser;
88
use crate::session_diagnostics;
99
use crate::session_diagnostics::UnsupportedLiteralReason;
1010

1111
pub(crate) struct DeprecationParser;
1212

13-
fn get(
14-
cx: &AcceptContext<'_>,
13+
fn get<S: Stage>(
14+
cx: &AcceptContext<'_, '_, S>,
1515
name: Symbol,
1616
param_span: Span,
1717
arg: &ArgParser<'_>,
@@ -41,19 +41,12 @@ fn get(
4141
}
4242
}
4343

44-
impl SingleAttributeParser for DeprecationParser {
45-
const PATH: &'static [Symbol] = &[sym::deprecated];
44+
impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
45+
const PATH: &[Symbol] = &[sym::deprecated];
46+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
47+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
4648

47-
fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span) {
48-
// FIXME(jdonszelmann): merge with errors from check_attrs.rs
49-
cx.emit_err(session_diagnostics::UnusedMultiple {
50-
this: cx.attr_span,
51-
other: first_span,
52-
name: sym::deprecated,
53-
});
54-
}
55-
56-
fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
49+
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
5750
let features = cx.features();
5851

5952
let mut since = None;

0 commit comments

Comments
 (0)