1
Fork 0

Replace infallible name_or_empty methods with fallible name methods.

I'm removing empty identifiers everywhere, because in practice they
always mean "no identifier" rather than "empty identifier". (An empty
identifier is impossible.) It's better to use `Option` to mean "no
identifier" because you then can't forget about the "no identifier"
possibility.

Some specifics:
- When testing an attribute for a single name, the commit uses the
  `has_name` method.
- When testing an attribute for multiple names, the commit uses the new
  `has_any_name` method.
- When using `match` on an attribute, the match arms now have `Some` on
  them.

In the tests, we now avoid printing empty identifiers by not printing
the identifier in the `error:` line at all, instead letting the carets
point out the problem.
This commit is contained in:
Nicholas Nethercote 2025-04-10 14:33:59 +10:00
parent 7e1f2f9c54
commit 2fef0a30ae
40 changed files with 217 additions and 203 deletions

View file

@ -404,7 +404,7 @@ passes_invalid_attr_at_crate_level =
passes_invalid_attr_at_crate_level_item =
the inner attribute doesn't annotate this {$kind}
passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export]` argument
passes_invalid_macro_export_arguments = invalid `#[macro_export]` argument
passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments
@ -771,8 +771,8 @@ passes_unreachable_due_to_uninhabited = unreachable {$descr}
.label_orig = any code following this expression is unreachable
.note = this expression has type `{$ty}`, which is uninhabited
passes_unrecognized_field =
unrecognized field name `{$name}`
passes_unrecognized_argument =
unrecognized argument
passes_unstable_attr_for_already_stable_feature =
can't mark as unstable using an already stable feature

View file

@ -9,7 +9,7 @@ use rustc_span::sym;
use rustc_target::callconv::FnAbi;
use super::layout_test::ensure_wf;
use crate::errors::{AbiInvalidAttribute, AbiNe, AbiOf, UnrecognizedField};
use crate::errors::{AbiInvalidAttribute, AbiNe, AbiOf, UnrecognizedArgument};
pub fn test_abi(tcx: TyCtxt<'_>) {
if !tcx.features().rustc_attrs() {
@ -77,8 +77,8 @@ fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut
// The `..` are the names of fields to dump.
let meta_items = attr.meta_item_list().unwrap_or_default();
for meta_item in meta_items {
match meta_item.name_or_empty() {
sym::debug => {
match meta_item.name() {
Some(sym::debug) => {
let fn_name = tcx.item_name(item_def_id.into());
tcx.dcx().emit_err(AbiOf {
span: tcx.def_span(item_def_id),
@ -88,8 +88,8 @@ fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut
});
}
name => {
tcx.dcx().emit_err(UnrecognizedField { span: meta_item.span(), name });
_ => {
tcx.dcx().emit_err(UnrecognizedArgument { span: meta_item.span() });
}
}
}
@ -118,8 +118,8 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut
}
let meta_items = attr.meta_item_list().unwrap_or_default();
for meta_item in meta_items {
match meta_item.name_or_empty() {
sym::debug => {
match meta_item.name() {
Some(sym::debug) => {
let ty::FnPtr(sig_tys, hdr) = ty.kind() else {
span_bug!(
meta_item.span(),
@ -138,7 +138,7 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut
let fn_name = tcx.item_name(item_def_id.into());
tcx.dcx().emit_err(AbiOf { span, fn_name, fn_abi: format!("{:#?}", abi) });
}
sym::assert_eq => {
Some(sym::assert_eq) => {
let ty::Tuple(fields) = ty.kind() else {
span_bug!(
meta_item.span(),
@ -188,8 +188,8 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut
});
}
}
name => {
tcx.dcx().emit_err(UnrecognizedField { span: meta_item.span(), name });
_ => {
tcx.dcx().emit_err(UnrecognizedArgument { span: meta_item.span() });
}
}
}

View file

@ -523,9 +523,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
fn check_no_sanitize(&self, attr: &Attribute, span: Span, target: Target) {
if let Some(list) = attr.meta_item_list() {
for item in list.iter() {
let sym = item.name_or_empty();
let sym = item.name();
match sym {
sym::address | sym::hwaddress => {
Some(s @ sym::address | s @ sym::hwaddress) => {
let is_valid =
matches!(target, Target::Fn | Target::Method(..) | Target::Static);
if !is_valid {
@ -533,7 +533,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
attr_span: item.span(),
defn_span: span,
accepted_kind: "a function or static",
attr_str: sym.as_str(),
attr_str: s.as_str(),
});
}
}
@ -544,7 +544,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
attr_span: item.span(),
defn_span: span,
accepted_kind: "a function",
attr_str: sym.as_str(),
attr_str: &match sym {
Some(name) => name.to_string(),
None => "...".to_string(),
},
});
}
}
@ -592,7 +595,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// * `#[track_caller]`
// * `#[test]`, `#[ignore]`, `#[should_panic]`
//
// NOTE: when making changes to this list, check that `error_codes/E0736.md` remains accurate
// NOTE: when making changes to this list, check that `error_codes/E0736.md` remains
// accurate.
const ALLOW_LIST: &[rustc_span::Symbol] = &[
// conditional compilation
sym::cfg_trace,
@ -675,11 +679,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
if !ALLOW_LIST.iter().any(|name| other_attr.has_name(*name)) {
if !other_attr.has_any_name(ALLOW_LIST) {
self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute {
span: other_attr.span(),
naked_span: attr.span(),
attr: other_attr.name_or_empty(),
attr: other_attr.name().unwrap(),
});
return;
@ -1153,7 +1157,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
) {
match target {
Target::Use | Target::ExternCrate => {
let do_inline = meta.name_or_empty() == sym::inline;
let do_inline = meta.has_name(sym::inline);
if let Some((prev_inline, prev_span)) = *specified_inline {
if do_inline != prev_inline {
let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
@ -1263,8 +1267,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
fn check_test_attr(&self, meta: &MetaItemInner, hir_id: HirId) {
if let Some(metas) = meta.meta_item_list() {
for i_meta in metas {
match (i_meta.name_or_empty(), i_meta.meta_item()) {
(sym::attr | sym::no_crate_inject, _) => {}
match (i_meta.name(), i_meta.meta_item()) {
(Some(sym::attr | sym::no_crate_inject), _) => {}
(_, Some(m)) => {
self.tcx.emit_node_span_lint(
INVALID_DOC_ATTRIBUTES,
@ -1325,61 +1329,63 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
if let Some(list) = attr.meta_item_list() {
for meta in &list {
if let Some(i_meta) = meta.meta_item() {
match i_meta.name_or_empty() {
sym::alias => {
match i_meta.name() {
Some(sym::alias) => {
if self.check_attr_not_crate_level(meta, hir_id, "alias") {
self.check_doc_alias(meta, hir_id, target, aliases);
}
}
sym::keyword => {
Some(sym::keyword) => {
if self.check_attr_not_crate_level(meta, hir_id, "keyword") {
self.check_doc_keyword(meta, hir_id);
}
}
sym::fake_variadic => {
Some(sym::fake_variadic) => {
if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") {
self.check_doc_fake_variadic(meta, hir_id);
}
}
sym::search_unbox => {
Some(sym::search_unbox) => {
if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") {
self.check_doc_search_unbox(meta, hir_id);
}
}
sym::test => {
Some(sym::test) => {
if self.check_attr_crate_level(attr, meta, hir_id) {
self.check_test_attr(meta, hir_id);
}
}
sym::html_favicon_url
| sym::html_logo_url
| sym::html_playground_url
| sym::issue_tracker_base_url
| sym::html_root_url
| sym::html_no_source => {
Some(
sym::html_favicon_url
| sym::html_logo_url
| sym::html_playground_url
| sym::issue_tracker_base_url
| sym::html_root_url
| sym::html_no_source,
) => {
self.check_attr_crate_level(attr, meta, hir_id);
}
sym::cfg_hide => {
Some(sym::cfg_hide) => {
if self.check_attr_crate_level(attr, meta, hir_id) {
self.check_doc_cfg_hide(meta, hir_id);
}
}
sym::inline | sym::no_inline => {
Some(sym::inline | sym::no_inline) => {
self.check_doc_inline(attr, meta, hir_id, target, specified_inline)
}
sym::masked => self.check_doc_masked(attr, meta, hir_id, target),
Some(sym::masked) => self.check_doc_masked(attr, meta, hir_id, target),
sym::cfg | sym::hidden | sym::notable_trait => {}
Some(sym::cfg | sym::hidden | sym::notable_trait) => {}
sym::rust_logo => {
Some(sym::rust_logo) => {
if self.check_attr_crate_level(attr, meta, hir_id)
&& !self.tcx.features().rustdoc_internals()
{
@ -2302,7 +2308,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
let name = attr.name_or_empty();
let name = attr.name().unwrap();
match target {
Target::ExternCrate | Target::Mod => {}
_ => {
@ -2334,12 +2340,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
attr.span(),
errors::MacroExport::TooManyItems,
);
} else if meta_item_list[0].name_or_empty() != sym::local_inner_macros {
} else if !meta_item_list[0].has_name(sym::local_inner_macros) {
self.tcx.emit_node_span_lint(
INVALID_MACRO_EXPORT_ARGUMENTS,
hir_id,
meta_item_list[0].span(),
errors::MacroExport::UnknownItem { name: meta_item_list[0].name_or_empty() },
errors::MacroExport::InvalidArgument,
);
}
} else {
@ -2384,33 +2390,28 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
// Warn on useless empty attributes.
let note = if (matches!(
attr.name_or_empty(),
sym::macro_use
| sym::allow
| sym::expect
| sym::warn
| sym::deny
| sym::forbid
| sym::feature
| sym::target_feature
) && attr.meta_item_list().is_some_and(|list| list.is_empty()))
let note = if attr.has_any_name(&[
sym::macro_use,
sym::allow,
sym::expect,
sym::warn,
sym::deny,
sym::forbid,
sym::feature,
sym::target_feature,
]) && attr.meta_item_list().is_some_and(|list| list.is_empty())
{
errors::UnusedNote::EmptyList { name: attr.name_or_empty() }
} else if matches!(
attr.name_or_empty(),
sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect
) && let Some(meta) = attr.meta_item_list()
errors::UnusedNote::EmptyList { name: attr.name().unwrap() }
} else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
&& let Some(meta) = attr.meta_item_list()
&& let [meta] = meta.as_slice()
&& let Some(item) = meta.meta_item()
&& let MetaItemKind::NameValue(_) = &item.kind
&& item.path == sym::reason
{
errors::UnusedNote::NoLints { name: attr.name_or_empty() }
} else if matches!(
attr.name_or_empty(),
sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect
) && let Some(meta) = attr.meta_item_list()
errors::UnusedNote::NoLints { name: attr.name().unwrap() }
} else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
&& let Some(meta) = attr.meta_item_list()
&& meta.iter().any(|meta| {
meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
})
@ -2443,7 +2444,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
return;
}
}
} else if attr.name_or_empty() == sym::default_method_body_is_const {
} else if attr.has_name(sym::default_method_body_is_const) {
errors::UnusedNote::DefaultMethodBodyConst
} else {
return;
@ -2900,10 +2901,11 @@ fn check_duplicates(
if matches!(duplicates, WarnFollowingWordOnly) && !attr.is_word() {
return;
}
let attr_name = attr.name().unwrap();
match duplicates {
DuplicatesOk => {}
WarnFollowing | FutureWarnFollowing | WarnFollowingWordOnly | FutureWarnPreceding => {
match seen.entry(attr.name_or_empty()) {
match seen.entry(attr_name) {
Entry::Occupied(mut entry) => {
let (this, other) = if matches!(duplicates, FutureWarnPreceding) {
let to_remove = entry.insert(attr.span());
@ -2930,7 +2932,7 @@ fn check_duplicates(
}
}
}
ErrorFollowing | ErrorPreceding => match seen.entry(attr.name_or_empty()) {
ErrorFollowing | ErrorPreceding => match seen.entry(attr_name) {
Entry::Occupied(mut entry) => {
let (this, other) = if matches!(duplicates, ErrorPreceding) {
let to_remove = entry.insert(attr.span());
@ -2938,11 +2940,7 @@ fn check_duplicates(
} else {
(attr.span(), *entry.get())
};
tcx.dcx().emit_err(errors::UnusedMultiple {
this,
other,
name: attr.name_or_empty(),
});
tcx.dcx().emit_err(errors::UnusedMultiple { this, other, name: attr_name });
}
Entry::Vacant(entry) => {
entry.insert(attr.span());

View file

@ -28,17 +28,17 @@ impl DebuggerVisualizerCollector<'_> {
return;
};
let (visualizer_type, visualizer_path) =
match (meta_item.name_or_empty(), meta_item.value_str()) {
(sym::natvis_file, Some(value)) => (DebuggerVisualizerType::Natvis, value),
(sym::gdb_script_file, Some(value)) => {
(DebuggerVisualizerType::GdbPrettyPrinter, value)
}
(_, _) => {
self.sess.dcx().emit_err(DebugVisualizerInvalid { span: meta_item.span });
return;
}
};
let (visualizer_type, visualizer_path) = match (meta_item.name(), meta_item.value_str())
{
(Some(sym::natvis_file), Some(value)) => (DebuggerVisualizerType::Natvis, value),
(Some(sym::gdb_script_file), Some(value)) => {
(DebuggerVisualizerType::GdbPrettyPrinter, value)
}
(_, _) => {
self.sess.dcx().emit_err(DebugVisualizerInvalid { span: meta_item.span });
return;
}
};
let file = match resolve_path(&self.sess, visualizer_path.as_str(), attr.span) {
Ok(file) => file,

View file

@ -756,7 +756,7 @@ pub(crate) enum MacroExport {
OnDeclMacro,
#[diag(passes_invalid_macro_export_arguments)]
UnknownItem { name: Symbol },
InvalidArgument,
#[diag(passes_invalid_macro_export_arguments_too_many_items)]
TooManyItems,
@ -1045,11 +1045,10 @@ pub(crate) struct AbiInvalidAttribute {
}
#[derive(Diagnostic)]
#[diag(passes_unrecognized_field)]
pub(crate) struct UnrecognizedField {
#[diag(passes_unrecognized_argument)]
pub(crate) struct UnrecognizedArgument {
#[primary_span]
pub span: Span,
pub name: Symbol,
}
#[derive(Diagnostic)]

View file

@ -13,7 +13,7 @@ use rustc_trait_selection::traits;
use crate::errors::{
LayoutAbi, LayoutAlign, LayoutHomogeneousAggregate, LayoutInvalidAttribute, LayoutOf,
LayoutSize, UnrecognizedField,
LayoutSize, UnrecognizedArgument,
};
pub fn test_layout(tcx: TyCtxt<'_>) {
@ -79,28 +79,28 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
// The `..` are the names of fields to dump.
let meta_items = attr.meta_item_list().unwrap_or_default();
for meta_item in meta_items {
match meta_item.name_or_empty() {
match meta_item.name() {
// FIXME: this never was about ABI and now this dump arg is confusing
sym::abi => {
Some(sym::abi) => {
tcx.dcx().emit_err(LayoutAbi {
span,
abi: format!("{:?}", ty_layout.backend_repr),
});
}
sym::align => {
Some(sym::align) => {
tcx.dcx().emit_err(LayoutAlign {
span,
align: format!("{:?}", ty_layout.align),
});
}
sym::size => {
Some(sym::size) => {
tcx.dcx()
.emit_err(LayoutSize { span, size: format!("{:?}", ty_layout.size) });
}
sym::homogeneous_aggregate => {
Some(sym::homogeneous_aggregate) => {
tcx.dcx().emit_err(LayoutHomogeneousAggregate {
span,
homogeneous_aggregate: format!(
@ -111,15 +111,15 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
});
}
sym::debug => {
Some(sym::debug) => {
let normalized_ty = tcx.normalize_erasing_regions(typing_env, ty);
// FIXME: using the `Debug` impl here isn't ideal.
let ty_layout = format!("{:#?}", *ty_layout);
tcx.dcx().emit_err(LayoutOf { span, normalized_ty, ty_layout });
}
name => {
tcx.dcx().emit_err(UnrecognizedField { span: meta_item.span(), name });
_ => {
tcx.dcx().emit_err(UnrecognizedArgument { span: meta_item.span() });
}
}
}