Skip to content

Commit bdb57ed

Browse files
Rewrite empty attribute lint
Signed-off-by: Jonathan Brouwer <[email protected]>
1 parent ad3b725 commit bdb57ed

File tree

20 files changed

+119
-110
lines changed

20 files changed

+119
-110
lines changed

compiler/rustc_attr_data_structures/src/attributes.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,6 @@ pub enum ReprAttr {
6767
ReprSimd,
6868
ReprTransparent,
6969
ReprAlign(Align),
70-
// this one is just so we can emit a lint for it
71-
ReprEmpty,
7270
}
7371
pub use ReprAttr::*;
7472

@@ -201,7 +199,7 @@ pub enum AttributeKind {
201199
AllowConstFnUnstable(ThinVec<Symbol>),
202200

203201
/// Represents `#[allow_internal_unstable]`.
204-
AllowInternalUnstable(ThinVec<(Symbol, Span)>),
202+
AllowInternalUnstable(ThinVec<(Symbol, Span)>, Span),
205203

206204
/// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint).
207205
AsPtr(Span),
@@ -285,7 +283,7 @@ pub enum AttributeKind {
285283
PubTransparent(Span),
286284

287285
/// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
288-
Repr(ThinVec<(ReprAttr, Span)>),
286+
Repr(ThinVec<(ReprAttr, Span)>, Span),
289287

290288
/// Represents `#[rustc_skip_during_method_dispatch]`.
291289
SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },

compiler/rustc_attr_data_structures/src/lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ pub struct AttributeLint<Id> {
1212
pub enum AttributeLintKind {
1313
UnusedDuplicate { this: Span, other: Span, warning: bool },
1414
IllFormedAttributeInput { suggestions: Vec<String> },
15+
EmptyAttribute { span: Span },
1516
}

compiler/rustc_attr_parsing/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ attr_parsing_deprecated_item_suggestion =
66
.help = add `#![feature(deprecated_suggestion)]` to the crate root
77
.note = see #94785 for more details
88
9+
attr_parsing_empty_attribute =
10+
unused attribute
11+
.suggestion = remove this attribute
12+
913
attr_parsing_empty_confusables =
1014
expected at least one confusable name
1115
attr_parsing_expected_one_cfg_pattern =

compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ pub(crate) struct AllowInternalUnstableParser;
1313
impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
1414
const PATH: &[Symbol] = &[sym::allow_internal_unstable];
1515
type Item = (Symbol, Span);
16-
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
16+
const CONVERT: ConvertFn<Self::Item> =
17+
ConvertFn::WithFirstAttributeSpan(AttributeKind::AllowInternalUnstable);
1718
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
1819

1920
fn extend<'c>(
@@ -30,7 +31,7 @@ pub(crate) struct AllowConstFnUnstableParser;
3031
impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
3132
const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
3233
type Item = Symbol;
33-
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
34+
const CONVERT: ConvertFn<Self::Item> = ConvertFn::Simple(AttributeKind::AllowConstFnUnstable);
3435
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
3536

3637
fn extend<'c>(

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,10 @@ pub(crate) enum AttributeOrder {
228228
KeepLast,
229229
}
230230

231-
type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind;
231+
pub(crate) enum ConvertFn<E> {
232+
Simple(fn(ThinVec<E>) -> AttributeKind),
233+
WithFirstAttributeSpan(fn(ThinVec<E>, Span) -> AttributeKind),
234+
}
232235

233236
/// Alternative to [`AttributeParser`] that automatically handles state management.
234237
/// If multiple attributes appear on an element, combines the values of each into a
@@ -262,22 +265,36 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
262265
pub(crate) struct Combine<T: CombineAttributeParser<S>, S: Stage>(
263266
PhantomData<(S, T)>,
264267
ThinVec<<T as CombineAttributeParser<S>>::Item>,
268+
Option<Span>,
265269
);
266270

267271
impl<T: CombineAttributeParser<S>, S: Stage> Default for Combine<T, S> {
268272
fn default() -> Self {
269-
Self(Default::default(), Default::default())
273+
Self(Default::default(), Default::default(), Default::default())
270274
}
271275
}
272276

273277
impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S> {
274278
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
275279
T::PATH,
276280
<T as CombineAttributeParser<S>>::TEMPLATE,
277-
|group: &mut Combine<T, S>, cx, args| group.1.extend(T::extend(cx, args)),
281+
|group: &mut Combine<T, S>, cx, args| {
282+
// Keep track of the span of the first attribute, for diagnostics
283+
if group.2.is_none() {
284+
group.2 = Some(cx.attr_span);
285+
}
286+
group.1.extend(T::extend(cx, args))
287+
},
278288
)];
279289

280290
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
281-
if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) }
291+
if let Some(first_span) = self.2 {
292+
Some(match T::CONVERT {
293+
ConvertFn::Simple(f) => f(self.1),
294+
ConvertFn::WithFirstAttributeSpan(f) => f(self.1, first_span),
295+
})
296+
} else {
297+
None
298+
}
282299
}
283300
}

compiler/rustc_attr_parsing/src/attributes/repr.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub(crate) struct ReprParser;
2323
impl<S: Stage> CombineAttributeParser<S> for ReprParser {
2424
type Item = (ReprAttr, Span);
2525
const PATH: &[Symbol] = &[sym::repr];
26-
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
26+
const CONVERT: ConvertFn<Self::Item> = ConvertFn::WithFirstAttributeSpan(AttributeKind::Repr);
2727
// FIXME(jdonszelmann): never used
2828
const TEMPLATE: AttributeTemplate =
2929
template!(List: "C | Rust | align(...) | packed(...) | <integer type> | transparent");
@@ -40,8 +40,8 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
4040
};
4141

4242
if list.is_empty() {
43-
// this is so validation can emit a lint
44-
reprs.push((ReprAttr::ReprEmpty, cx.attr_span));
43+
cx.warn_empty_attribute(cx.attr_span);
44+
return reprs;
4545
}
4646

4747
for param in list.mixed() {

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ mod private {
147147
#[allow(private_interfaces)]
148148
pub trait Stage: Sized + 'static + Sealed {
149149
type Id: Copy;
150+
const SHOULD_EMIT_LINTS: bool;
150151

151152
fn parsers() -> &'static group_type!(Self);
152153

@@ -157,6 +158,7 @@ pub trait Stage: Sized + 'static + Sealed {
157158
#[allow(private_interfaces)]
158159
impl Stage for Early {
159160
type Id = NodeId;
161+
const SHOULD_EMIT_LINTS: bool = false;
160162

161163
fn parsers() -> &'static group_type!(Self) {
162164
&early::ATTRIBUTE_PARSERS
@@ -170,6 +172,7 @@ impl Stage for Early {
170172
#[allow(private_interfaces)]
171173
impl Stage for Late {
172174
type Id = HirId;
175+
const SHOULD_EMIT_LINTS: bool = true;
173176

174177
fn parsers() -> &'static group_type!(Self) {
175178
&late::ATTRIBUTE_PARSERS
@@ -210,6 +213,9 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
210213
/// must be delayed until after HIR is built. This method will take care of the details of
211214
/// that.
212215
pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
216+
if !S::SHOULD_EMIT_LINTS {
217+
return;
218+
}
213219
let id = self.target_id;
214220
(self.emit_lint)(AttributeLint { id, span, kind: lint });
215221
}
@@ -381,6 +387,10 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
381387
},
382388
})
383389
}
390+
391+
pub(crate) fn warn_empty_attribute(&mut self, span: Span) {
392+
self.emit_lint(AttributeLintKind::EmptyAttribute { span }, span);
393+
}
384394
}
385395

386396
impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {

compiler/rustc_attr_parsing/src/lints.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,11 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emi
2828
},
2929
);
3030
}
31+
AttributeLintKind::EmptyAttribute { span } => lint_emitter.emit_node_span_lint(
32+
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
33+
*id,
34+
*span,
35+
session_diagnostics::EmptyAttributeList { attr_span: *span },
36+
),
3137
}
3238
}

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,13 @@ pub(crate) struct EmptyConfusables {
466466
pub span: Span,
467467
}
468468

469+
#[derive(LintDiagnostic)]
470+
#[diag(attr_parsing_empty_attribute)]
471+
pub(crate) struct EmptyAttributeList {
472+
#[suggestion(code = "", applicability = "machine-applicable")]
473+
pub attr_span: Span,
474+
}
475+
469476
#[derive(Diagnostic)]
470477
#[diag(attr_parsing_invalid_alignment_value, code = E0589)]
471478
pub(crate) struct InvalidAlignmentValue {

compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ impl<'a> TraitDef<'a> {
485485
Annotatable::Item(item) => {
486486
let is_packed = matches!(
487487
AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, item.id),
488-
Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(x, _)| matches!(x, ReprPacked(..)))
488+
Some(Attribute::Parsed(AttributeKind::Repr(r, _))) if r.iter().any(|(x, _)| matches!(x, ReprPacked(..)))
489489
);
490490

491491
let newitem = match &item.kind {

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
109109

110110
if let hir::Attribute::Parsed(p) = attr {
111111
match p {
112-
AttributeKind::Repr(reprs) => {
112+
AttributeKind::Repr(reprs, _) => {
113113
codegen_fn_attrs.alignment = reprs
114114
.iter()
115115
.filter_map(

compiler/rustc_expand/src/base.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -880,7 +880,7 @@ impl SyntaxExtension {
880880
is_local: bool,
881881
) -> SyntaxExtension {
882882
let allow_internal_unstable =
883-
find_attr!(attrs, AttributeKind::AllowInternalUnstable(i) => i)
883+
find_attr!(attrs, AttributeKind::AllowInternalUnstable(i, _) => i)
884884
.map(|i| i.as_slice())
885885
.unwrap_or_default();
886886
// FIXME(jdonszelman): allow_internal_unsafe isn't yet new-style

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,7 +1224,7 @@ pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
12241224
let repr = def.repr();
12251225
if repr.packed() {
12261226
if let Some(reprs) =
1227-
attrs::find_attr!(tcx.get_all_attrs(def.did()), attrs::AttributeKind::Repr(r) => r)
1227+
attrs::find_attr!(tcx.get_all_attrs(def.did()), attrs::AttributeKind::Repr(r, _) => r)
12281228
{
12291229
for (r, _) in reprs {
12301230
if let ReprPacked(pack) = r
@@ -1447,7 +1447,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
14471447
if def.variants().is_empty() {
14481448
attrs::find_attr!(
14491449
tcx.get_all_attrs(def_id),
1450-
attrs::AttributeKind::Repr(rs) => {
1450+
attrs::AttributeKind::Repr(rs, _) => {
14511451
struct_span_code_err!(
14521452
tcx.dcx(),
14531453
rs.first().unwrap().1,

compiler/rustc_lint/src/nonstandard_style.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ impl EarlyLintPass for NonCamelCaseTypes {
168168
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
169169
let has_repr_c = matches!(
170170
AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, it.id),
171-
Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC)
171+
Some(Attribute::Parsed(AttributeKind::Repr(r, _))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC)
172172
);
173173

174174
if has_repr_c {

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,7 +1521,8 @@ impl<'tcx> TyCtxt<'tcx> {
15211521
field_shuffle_seed ^= user_seed;
15221522
}
15231523

1524-
if let Some(reprs) = attr::find_attr!(self.get_all_attrs(did), AttributeKind::Repr(r) => r)
1524+
if let Some(reprs) =
1525+
attr::find_attr!(self.get_all_attrs(did), AttributeKind::Repr(r, _) => r)
15251526
{
15261527
for (r, _) in reprs {
15271528
flags.insert(match *r {
@@ -1562,10 +1563,6 @@ impl<'tcx> TyCtxt<'tcx> {
15621563
max_align = max_align.max(Some(align));
15631564
ReprFlags::empty()
15641565
}
1565-
attr::ReprEmpty => {
1566-
/* skip these, they're just for diagnostics */
1567-
ReprFlags::empty()
1568-
}
15691566
});
15701567
}
15711568
}

0 commit comments

Comments
 (0)