Rollup merge of #143252 - JonathanBrouwer:rewrite_empty_attribute, r=jdonszelmann
Rewrite empty attribute lint for new attribute parser cc `@jdonszelmann`
This commit is contained in:
commit
017fe2fb8f
29 changed files with 204 additions and 202 deletions
|
|
@ -67,8 +67,6 @@ pub enum ReprAttr {
|
|||
ReprSimd,
|
||||
ReprTransparent,
|
||||
ReprAlign(Align),
|
||||
// this one is just so we can emit a lint for it
|
||||
ReprEmpty,
|
||||
}
|
||||
pub use ReprAttr::*;
|
||||
|
||||
|
|
@ -304,7 +302,7 @@ pub enum AttributeKind {
|
|||
PubTransparent(Span),
|
||||
|
||||
/// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
|
||||
Repr(ThinVec<(ReprAttr, Span)>),
|
||||
Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span },
|
||||
|
||||
/// Represents `#[rustc_layout_scalar_valid_range_end]`.
|
||||
RustcLayoutScalarValidRangeEnd(Box<u128>, Span),
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ impl AttributeKind {
|
|||
Optimize(..) => No,
|
||||
PassByValue(..) => Yes,
|
||||
PubTransparent(..) => Yes,
|
||||
Repr(..) => No,
|
||||
Repr { .. } => No,
|
||||
RustcLayoutScalarValidRangeEnd(..) => Yes,
|
||||
RustcLayoutScalarValidRangeStart(..) => Yes,
|
||||
RustcObjectLifetimeDefault => No,
|
||||
|
|
|
|||
|
|
@ -12,4 +12,5 @@ pub struct AttributeLint<Id> {
|
|||
pub enum AttributeLintKind {
|
||||
UnusedDuplicate { this: Span, other: Span, warning: bool },
|
||||
IllFormedAttributeInput { suggestions: Vec<String> },
|
||||
EmptyAttribute { first_span: Span },
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ attr_parsing_deprecated_item_suggestion =
|
|||
.help = add `#![feature(deprecated_suggestion)]` to the crate root
|
||||
.note = see #94785 for more details
|
||||
|
||||
attr_parsing_empty_attribute =
|
||||
unused attribute
|
||||
.suggestion = remove this attribute
|
||||
|
||||
attr_parsing_empty_confusables =
|
||||
expected at least one confusable name
|
||||
attr_parsing_expected_one_cfg_pattern =
|
||||
|
|
|
|||
|
|
@ -298,6 +298,10 @@ impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
|
|||
cx.expected_list(cx.attr_span);
|
||||
return features;
|
||||
};
|
||||
if list.is_empty() {
|
||||
cx.warn_empty_attribute(cx.attr_span);
|
||||
return features;
|
||||
}
|
||||
for item in list.mixed() {
|
||||
let Some(name_value) = item.meta_item() else {
|
||||
cx.expected_name_value(item.span(), Some(sym::enable));
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ pub(crate) struct ReprParser;
|
|||
impl<S: Stage> CombineAttributeParser<S> for ReprParser {
|
||||
type Item = (ReprAttr, Span);
|
||||
const PATH: &[Symbol] = &[sym::repr];
|
||||
const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::Repr(items);
|
||||
const CONVERT: ConvertFn<Self::Item> =
|
||||
|items, first_span| AttributeKind::Repr { reprs: items, first_span };
|
||||
// FIXME(jdonszelmann): never used
|
||||
const TEMPLATE: AttributeTemplate =
|
||||
template!(List: "C | Rust | align(...) | packed(...) | <integer type> | transparent");
|
||||
|
|
@ -40,8 +41,8 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
|
|||
};
|
||||
|
||||
if list.is_empty() {
|
||||
// this is so validation can emit a lint
|
||||
reprs.push((ReprAttr::ReprEmpty, cx.attr_span));
|
||||
cx.warn_empty_attribute(cx.attr_span);
|
||||
return reprs;
|
||||
}
|
||||
|
||||
for param in list.mixed() {
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@ mod private {
|
|||
#[allow(private_interfaces)]
|
||||
pub trait Stage: Sized + 'static + Sealed {
|
||||
type Id: Copy;
|
||||
const SHOULD_EMIT_LINTS: bool;
|
||||
|
||||
fn parsers() -> &'static group_type!(Self);
|
||||
|
||||
|
|
@ -175,6 +176,7 @@ pub trait Stage: Sized + 'static + Sealed {
|
|||
#[allow(private_interfaces)]
|
||||
impl Stage for Early {
|
||||
type Id = NodeId;
|
||||
const SHOULD_EMIT_LINTS: bool = false;
|
||||
|
||||
fn parsers() -> &'static group_type!(Self) {
|
||||
&early::ATTRIBUTE_PARSERS
|
||||
|
|
@ -188,6 +190,7 @@ impl Stage for Early {
|
|||
#[allow(private_interfaces)]
|
||||
impl Stage for Late {
|
||||
type Id = HirId;
|
||||
const SHOULD_EMIT_LINTS: bool = true;
|
||||
|
||||
fn parsers() -> &'static group_type!(Self) {
|
||||
&late::ATTRIBUTE_PARSERS
|
||||
|
|
@ -228,6 +231,9 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
|
|||
/// must be delayed until after HIR is built. This method will take care of the details of
|
||||
/// that.
|
||||
pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
|
||||
if !S::SHOULD_EMIT_LINTS {
|
||||
return;
|
||||
}
|
||||
let id = self.target_id;
|
||||
(self.emit_lint)(AttributeLint { id, span, kind: lint });
|
||||
}
|
||||
|
|
@ -409,6 +415,10 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn warn_empty_attribute(&mut self, span: Span) {
|
||||
self.emit_lint(AttributeLintKind::EmptyAttribute { first_span: span }, span);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
|
||||
|
|
|
|||
|
|
@ -28,5 +28,11 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emi
|
|||
},
|
||||
);
|
||||
}
|
||||
AttributeLintKind::EmptyAttribute { first_span } => lint_emitter.emit_node_span_lint(
|
||||
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
|
||||
*id,
|
||||
*first_span,
|
||||
session_diagnostics::EmptyAttributeList { attr_span: *first_span },
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -473,6 +473,13 @@ pub(crate) struct EmptyConfusables {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(attr_parsing_empty_attribute)]
|
||||
pub(crate) struct EmptyAttributeList {
|
||||
#[suggestion(code = "", applicability = "machine-applicable")]
|
||||
pub attr_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_invalid_alignment_value, code = E0589)]
|
||||
pub(crate) struct InvalidAlignmentValue {
|
||||
|
|
|
|||
|
|
@ -485,7 +485,7 @@ impl<'a> TraitDef<'a> {
|
|||
Annotatable::Item(item) => {
|
||||
let is_packed = matches!(
|
||||
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(..)))
|
||||
Some(Attribute::Parsed(AttributeKind::Repr { reprs, .. })) if reprs.iter().any(|(x, _)| matches!(x, ReprPacked(..)))
|
||||
);
|
||||
|
||||
let newitem = match &item.kind {
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
|
||||
if let hir::Attribute::Parsed(p) = attr {
|
||||
match p {
|
||||
AttributeKind::Repr(reprs) => {
|
||||
AttributeKind::Repr { reprs, first_span: _ } => {
|
||||
codegen_fn_attrs.alignment = reprs
|
||||
.iter()
|
||||
.filter_map(
|
||||
|
|
|
|||
|
|
@ -1395,8 +1395,7 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
|
|||
pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
|
||||
let repr = def.repr();
|
||||
if repr.packed() {
|
||||
if let Some(reprs) =
|
||||
attrs::find_attr!(tcx.get_all_attrs(def.did()), attrs::AttributeKind::Repr(r) => r)
|
||||
if let Some(reprs) = attrs::find_attr!(tcx.get_all_attrs(def.did()), attrs::AttributeKind::Repr { reprs, .. } => reprs)
|
||||
{
|
||||
for (r, _) in reprs {
|
||||
if let ReprPacked(pack) = r
|
||||
|
|
@ -1619,10 +1618,10 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
|||
if def.variants().is_empty() {
|
||||
attrs::find_attr!(
|
||||
tcx.get_all_attrs(def_id),
|
||||
attrs::AttributeKind::Repr(rs) => {
|
||||
attrs::AttributeKind::Repr { reprs, first_span } => {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
rs.first().unwrap().1,
|
||||
reprs.first().map(|repr| repr.1).unwrap_or(*first_span),
|
||||
E0084,
|
||||
"unsupported representation for zero-variant enum"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ 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, it.id),
|
||||
Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC)
|
||||
Some(Attribute::Parsed(AttributeKind::Repr { reprs, ..})) if reprs.iter().any(|(r, _)| r == &ReprAttr::ReprC)
|
||||
);
|
||||
|
||||
if has_repr_c {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok
|
|||
__p.word_space(",");
|
||||
}
|
||||
__p.word(#string_name);
|
||||
__p.word_space(":");
|
||||
__p.word(":");
|
||||
__p.nbsp();
|
||||
__printed_anything = true;
|
||||
}
|
||||
#name.print_attribute(__p);
|
||||
|
|
|
|||
|
|
@ -1525,7 +1525,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
field_shuffle_seed ^= user_seed;
|
||||
}
|
||||
|
||||
if let Some(reprs) = attr::find_attr!(self.get_all_attrs(did), AttributeKind::Repr(r) => r)
|
||||
if let Some(reprs) =
|
||||
attr::find_attr!(self.get_all_attrs(did), AttributeKind::Repr { reprs, .. } => reprs)
|
||||
{
|
||||
for (r, _) in reprs {
|
||||
flags.insert(match *r {
|
||||
|
|
@ -1566,10 +1567,6 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
max_align = max_align.max(Some(align));
|
||||
ReprFlags::empty()
|
||||
}
|
||||
attr::ReprEmpty => {
|
||||
/* skip these, they're just for diagnostics */
|
||||
ReprFlags::empty()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
Attribute::Parsed(AttributeKind::DocComment { .. }) => { /* `#[doc]` is actually a lot more than just doc comments, so is checked below*/
|
||||
}
|
||||
Attribute::Parsed(AttributeKind::Repr(_)) => { /* handled below this loop and elsewhere */
|
||||
Attribute::Parsed(AttributeKind::Repr { .. }) => { /* handled below this loop and elsewhere */
|
||||
}
|
||||
Attribute::Parsed(AttributeKind::RustcObjectLifetimeDefault) => {
|
||||
self.check_object_lifetime_default(hir_id);
|
||||
|
|
@ -1948,7 +1948,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
// #[repr(foo)]
|
||||
// #[repr(bar, align(8))]
|
||||
// ```
|
||||
let reprs = find_attr!(attrs, AttributeKind::Repr(r) => r.as_slice()).unwrap_or(&[]);
|
||||
let (reprs, first_attr_span) = find_attr!(attrs, AttributeKind::Repr { reprs, first_span } => (reprs.as_slice(), Some(*first_span))).unwrap_or((&[], None));
|
||||
|
||||
let mut int_reprs = 0;
|
||||
let mut is_explicit_rust = false;
|
||||
|
|
@ -2045,33 +2045,32 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
// FIXME(jdonszelmann): move the diagnostic for unused repr attrs here, I think
|
||||
// it's a better place for it.
|
||||
ReprAttr::ReprEmpty => {
|
||||
// catch `repr()` with no arguments, applied to an item (i.e. not `#![repr()]`)
|
||||
if item.is_some() {
|
||||
match target {
|
||||
Target::Struct | Target::Union | Target::Enum => continue,
|
||||
Target::Fn | Target::Method(_) => {
|
||||
self.dcx().emit_err(errors::ReprAlignShouldBeAlign {
|
||||
span: *repr_span,
|
||||
item: target.name(),
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
|
||||
hint_span: *repr_span,
|
||||
span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// catch `repr()` with no arguments, applied to an item (i.e. not `#![repr()]`)
|
||||
if let Some(first_attr_span) = first_attr_span
|
||||
&& reprs.is_empty()
|
||||
&& item.is_some()
|
||||
{
|
||||
match target {
|
||||
Target::Struct | Target::Union | Target::Enum => {}
|
||||
Target::Fn | Target::Method(_) => {
|
||||
self.dcx().emit_err(errors::ReprAlignShouldBeAlign {
|
||||
span: first_attr_span,
|
||||
item: target.name(),
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
|
||||
hint_span: first_attr_span,
|
||||
span,
|
||||
});
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Just point at all repr hints if there are any incompatibilities.
|
||||
// This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
|
||||
let hint_spans = reprs.iter().map(|(_, span)| *span);
|
||||
|
|
@ -2324,43 +2323,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
|
||||
fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) {
|
||||
// FIXME(jdonszelmann): deduplicate these checks after more attrs are parsed. This is very
|
||||
// ugly now but can 100% be removed later.
|
||||
if let Attribute::Parsed(p) = attr {
|
||||
match p {
|
||||
AttributeKind::Repr(reprs) => {
|
||||
for (r, span) in reprs {
|
||||
if let ReprAttr::ReprEmpty = r {
|
||||
self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
*span,
|
||||
errors::Unused {
|
||||
attr_span: *span,
|
||||
note: errors::UnusedNote::EmptyList { name: sym::repr },
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
AttributeKind::TargetFeature(features, span) if features.len() == 0 => {
|
||||
self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
*span,
|
||||
errors::Unused {
|
||||
attr_span: *span,
|
||||
note: errors::UnusedNote::EmptyList { name: sym::target_feature },
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
// Warn on useless empty attributes.
|
||||
// FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
|
||||
let note = if attr.has_any_name(&[
|
||||
sym::macro_use,
|
||||
sym::allow,
|
||||
|
|
@ -2576,7 +2540,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
|
||||
fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attribute]) {
|
||||
if !find_attr!(attrs, AttributeKind::Repr(r) => r.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent))
|
||||
if !find_attr!(attrs, AttributeKind::Repr { reprs, .. } => reprs.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
self.dcx().emit_err(errors::RustcPubTransparent { span, attr_span });
|
||||
|
|
@ -2852,8 +2816,12 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
|
|||
ATTRS_TO_CHECK.iter().find(|attr_to_check| attr.has_name(**attr_to_check))
|
||||
{
|
||||
(attr.span(), *a)
|
||||
} else if let Attribute::Parsed(AttributeKind::Repr(r)) = attr {
|
||||
(r.first().unwrap().1, sym::repr)
|
||||
} else if let Attribute::Parsed(AttributeKind::Repr {
|
||||
reprs: _,
|
||||
first_span: first_attr_span,
|
||||
}) = attr
|
||||
{
|
||||
(*first_attr_span, sym::repr)
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue