From 02b516afd99d5796672543e50529a002200858ff Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 1 Apr 2026 14:30:44 -0400 Subject: [PATCH 01/11] core: Update the feature gate on `TryFrom for bool` This implementation was added recently in f12288ec2632 ("TryFrom for bool") but used an old feature gate and stabilization version. Update to reflect when these were actually added. --- library/core/src/convert/num.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index b80980219b1e2..673245056e79b 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -330,7 +330,7 @@ macro_rules! impl_try_from_both_bounded { /// Implement `TryFrom` for `bool` macro_rules! impl_try_from_integer_for_bool { ($($int:ty)+) => {$( - #[stable(feature = "try_from", since = "1.34.0")] + #[stable(feature = "bool_try_from_int", since = "1.95.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const TryFrom<$int> for bool { type Error = TryFromIntError; From 8863db85833e22b52a546b518b8e19b2f556fa36 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Sun, 29 Mar 2026 08:56:44 +0000 Subject: [PATCH 02/11] add broken test for multiple cfg arguments --- tests/ui/cfg/suggest-any-or-all.rs | 9 +++++++++ tests/ui/cfg/suggest-any-or-all.stderr | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/ui/cfg/suggest-any-or-all.rs create mode 100644 tests/ui/cfg/suggest-any-or-all.stderr diff --git a/tests/ui/cfg/suggest-any-or-all.rs b/tests/ui/cfg/suggest-any-or-all.rs new file mode 100644 index 0000000000000..bade129643a0d --- /dev/null +++ b/tests/ui/cfg/suggest-any-or-all.rs @@ -0,0 +1,9 @@ +#[cfg(foo, bar)] +//~^ ERROR malformed `cfg` attribute input +fn f() {} + +#[cfg()] +//~^ ERROR malformed `cfg` attribute input +struct Foo; + +fn main() {} diff --git a/tests/ui/cfg/suggest-any-or-all.stderr b/tests/ui/cfg/suggest-any-or-all.stderr new file mode 100644 index 0000000000000..596a49f261036 --- /dev/null +++ b/tests/ui/cfg/suggest-any-or-all.stderr @@ -0,0 +1,25 @@ +error[E0805]: malformed `cfg` attribute input + --> $DIR/suggest-any-or-all.rs:1:1 + | +LL | #[cfg(foo, bar)] + | ^^^^^----------^ + | | | + | | expected a single argument here + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + +error[E0805]: malformed `cfg` attribute input + --> $DIR/suggest-any-or-all.rs:5:1 + | +LL | #[cfg()] + | ^^^^^--^ + | | | + | | expected a single argument here + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0805`. From 7199a0bb9a730d253542d50fcf91bfeedeb5531f Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 1 Apr 2026 15:12:16 -0500 Subject: [PATCH 03/11] rustdoc: fix href of extern crates in search results --- src/librustdoc/html/render/search_index.rs | 29 ++++++++++++++++------ src/librustdoc/html/static/js/rustdoc.d.ts | 2 +- src/librustdoc/html/static/js/search.js | 13 +++++++--- tests/rustdoc-js/import-filter.js | 14 ++++++++--- tests/rustdoc-js/renamed-crate-148300.js | 12 +++++++++ tests/rustdoc-js/renamed-crate-148300.rs | 1 + 6 files changed, 54 insertions(+), 17 deletions(-) create mode 100644 tests/rustdoc-js/renamed-crate-148300.js create mode 100644 tests/rustdoc-js/renamed-crate-148300.rs diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 5f69cc030e266..935fe258c8076 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -13,6 +13,7 @@ use ::serde::{Deserialize, Serialize}; use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::thin_vec::ThinVec; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::find_attr; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; @@ -22,7 +23,7 @@ use stringdex::internals as stringdex_internals; use tracing::instrument; use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate}; -use crate::clean::{self, utils}; +use crate::clean::{self, ExternalLocation, utils}; use crate::config::ShouldMerge; use crate::error::Error; use crate::formats::cache::{Cache, OrphanImplItem}; @@ -616,7 +617,8 @@ impl SerializedSearchIndex { trait_parent, deprecated, unstable, - associated_item_disambiguator, + associated_item_disambiguator_or_extern_crate_url: + associated_item_disambiguator, }| EntryData { krate: *map.get(krate).unwrap(), ty: *ty, @@ -627,7 +629,8 @@ impl SerializedSearchIndex { trait_parent: trait_parent.and_then(|path_id| map.get(&path_id).copied()), deprecated: *deprecated, unstable: *unstable, - associated_item_disambiguator: associated_item_disambiguator.clone(), + associated_item_disambiguator_or_extern_crate_url: + associated_item_disambiguator.clone(), }, ), self.descs[id].clone(), @@ -898,7 +901,7 @@ struct EntryData { trait_parent: Option, deprecated: bool, unstable: bool, - associated_item_disambiguator: Option, + associated_item_disambiguator_or_extern_crate_url: Option, } impl Serialize for EntryData { @@ -915,7 +918,7 @@ impl Serialize for EntryData { seq.serialize_element(&self.trait_parent.map(|id| id + 1).unwrap_or(0))?; seq.serialize_element(&if self.deprecated { 1 } else { 0 })?; seq.serialize_element(&if self.unstable { 1 } else { 0 })?; - if let Some(disambig) = &self.associated_item_disambiguator { + if let Some(disambig) = &self.associated_item_disambiguator_or_extern_crate_url { seq.serialize_element(&disambig)?; } seq.end() @@ -961,7 +964,8 @@ impl<'de> Deserialize<'de> for EntryData { trait_parent: Option::::from(trait_parent).map(|path| path as usize), deprecated: deprecated != 0, unstable: unstable != 0, - associated_item_disambiguator, + associated_item_disambiguator_or_extern_crate_url: + associated_item_disambiguator, }) } } @@ -1389,7 +1393,7 @@ pub(crate) fn build_index( trait_parent: None, deprecated: false, unstable: false, - associated_item_disambiguator: None, + associated_item_disambiguator_or_extern_crate_url: None, }), crate_doc, None, @@ -1528,7 +1532,8 @@ pub(crate) fn build_index( exact_module_path, deprecated: item.is_deprecated, unstable: item.is_unstable, - associated_item_disambiguator: if let Some(impl_id) = item.impl_id + associated_item_disambiguator_or_extern_crate_url: if let Some(impl_id) = + item.impl_id && let Some(parent_idx) = item.parent_idx && associated_item_duplicates .get(&(parent_idx, item.ty, item.name)) @@ -1537,6 +1542,14 @@ pub(crate) fn build_index( > 1 { Some(render::get_id_for_impl(tcx, ItemId::DefId(impl_id))) + } else if item.ty == ItemType::ExternCrate + && let Some(local_def_id) = item.defid.and_then(|def_id| def_id.as_local()) + && let cnum = tcx.extern_mod_stmt_cnum(local_def_id).unwrap_or(LOCAL_CRATE) + && let Some(ExternalLocation::Remote { url, is_absolute }) = + cache.extern_locations.get(&cnum) + && *is_absolute + { + Some(format!("{}{}", url, tcx.crate_name(cnum).as_str())) } else { None }, diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 3857e6e9fd878..2d2baf22e0f63 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -244,7 +244,7 @@ declare namespace rustdoc { traitParent: number?, deprecated: boolean, unstable: boolean, - associatedItemDisambiguator: string?, + associatedItemDisambiguatorOrExternCrateUrl: string?, } /** diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index e540b3942d249..85a76054a12dd 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1657,7 +1657,7 @@ class DocSearch { traitParent: raw[5] === 0 ? null : raw[5] - 1, deprecated: raw[6] === 1 ? true : false, unstable: raw[7] === 1 ? true : false, - associatedItemDisambiguator: raw.length === 8 ? null : raw[8], + associatedItemDisambiguatorOrExternCrateUrl: raw.length === 8 ? null : raw[8], }; } @@ -2176,7 +2176,12 @@ class DocSearch { "/" + type + "." + name + ".html"; } else if (type === "externcrate") { displayPath = ""; - href = this.rootPath + name + "/index.html"; + let base = this.rootPath + name; + if (item.entry && item.entry.associatedItemDisambiguatorOrExternCrateUrl) { + base = item.entry.associatedItemDisambiguatorOrExternCrateUrl; + } + + href = base + "/index.html"; } else if (item.parent) { const myparent = item.parent; let anchor = type + "." + name; @@ -2201,8 +2206,8 @@ class DocSearch { } else { displayPath = path + "::" + myparent.name + "::"; } - if (item.entry && item.entry.associatedItemDisambiguator !== null) { - anchor = item.entry.associatedItemDisambiguator + "/" + anchor; + if (item.entry && item.entry.associatedItemDisambiguatorOrExternCrateUrl !== null) { + anchor = item.entry.associatedItemDisambiguatorOrExternCrateUrl + "/" + anchor; } href = this.rootPath + path.replace(/::/g, "/") + "/" + pageType + diff --git a/tests/rustdoc-js/import-filter.js b/tests/rustdoc-js/import-filter.js index 408d0e3326179..ced41bd7e2a2b 100644 --- a/tests/rustdoc-js/import-filter.js +++ b/tests/rustdoc-js/import-filter.js @@ -6,15 +6,21 @@ const EXPECTED = [ 'query': 'import:st', 'others': [ { 'path': 'foo', 'name': 'st', 'href': '../foo/index.html#reexport.st' }, - // FIXME: `href` is wrong: - { 'path': 'foo', 'name': 'st2', 'href': '../st2/index.html' }, + { + 'path': 'foo', + 'name': 'st2', + 'href': 'https://doc.rust-lang.org/nightly/std/index.html' + }, ], }, { 'query': 'externcrate:st', 'others': [ - // FIXME: `href` is wrong: - { 'path': 'foo', 'name': 'st2', 'href': '../st2/index.html' }, + { + 'path': 'foo', + 'name': 'st2', + 'href': 'https://doc.rust-lang.org/nightly/std/index.html' + }, ], }, ]; diff --git a/tests/rustdoc-js/renamed-crate-148300.js b/tests/rustdoc-js/renamed-crate-148300.js new file mode 100644 index 0000000000000..9ac4e65631b77 --- /dev/null +++ b/tests/rustdoc-js/renamed-crate-148300.js @@ -0,0 +1,12 @@ +// Regression test for +// +// This ensures that extern crates in search results link to the correct url. + +const EXPECTED = [ + { + query: 'st2', + others: [ + { name: 'st2', href: 'https://doc.rust-lang.org/nightly/std/index.html' } + ], + }, +]; diff --git a/tests/rustdoc-js/renamed-crate-148300.rs b/tests/rustdoc-js/renamed-crate-148300.rs new file mode 100644 index 0000000000000..9bb54582e2f10 --- /dev/null +++ b/tests/rustdoc-js/renamed-crate-148300.rs @@ -0,0 +1 @@ +pub extern crate std as st2; From 3888a633b7d594dc77c63854daaa7ef5f754b7d4 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Thu, 26 Mar 2026 11:53:17 +0000 Subject: [PATCH 04/11] Add a regression test for rustdoc ICEing on negative `Deref`/`DerefMut` impls --- tests/rustdoc-ui/deref/negative-deref-ice-128801.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/rustdoc-ui/deref/negative-deref-ice-128801.rs diff --git a/tests/rustdoc-ui/deref/negative-deref-ice-128801.rs b/tests/rustdoc-ui/deref/negative-deref-ice-128801.rs new file mode 100644 index 0000000000000..439727f3d3d32 --- /dev/null +++ b/tests/rustdoc-ui/deref/negative-deref-ice-128801.rs @@ -0,0 +1,11 @@ +//@ check-pass + +// Regression test for https://github.com/rust-lang/rust/issues/128801 +// Negative `Deref`/`DerefMut` impls should not cause an ICE. + +#![feature(negative_impls)] + +pub struct Source; + +impl !std::ops::Deref for Source {} +impl !std::ops::DerefMut for Source {} From 23b1e78028a57a55acffb93588dcba4cbf3457a3 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Thu, 26 Mar 2026 22:05:05 +0000 Subject: [PATCH 05/11] rustdoc: When collecting `Deref` impls with their targets, skip the negative ones rustdoc assumed every `Deref` impl has an associated `Target` type, but negative impls (e.g. `impl !Deref for T {}`) have none. Skip them in both the trait-impl collection pass and the HTML render pass to avoid panicking on the missing Target. --- src/librustdoc/html/render/mod.rs | 5 +++-- src/librustdoc/passes/collect_trait_impls.rs | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 556d383a0e9fb..c0c380447f2cb 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1517,8 +1517,9 @@ fn render_assoc_items_inner( } if !traits.is_empty() { - let deref_impl = - traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait()); + let deref_impl = traits.iter().find(|t| { + t.trait_did() == cx.tcx().lang_items().deref_trait() && !t.is_negative_trait_impl() + }); if let Some(impl_) = deref_impl { let has_deref_mut = traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait()); diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index b58daceed0702..251efa15e240a 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -155,8 +155,9 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> // scan through included items ahead of time to splice in Deref targets to the "valid" sets for it in new_items_external.iter().chain(new_items_local.iter()) { - if let ImplItem(box Impl { ref for_, ref trait_, ref items, .. }) = it.kind + if let ImplItem(box Impl { ref for_, ref trait_, ref items, polarity, .. }) = it.kind && trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() + && polarity != ty::ImplPolarity::Negative && cleaner.keep_impl(for_, true) { let target = items From 02a73dbe9b98cd0ddb28e6dbd2439534e26c211a Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Fri, 27 Mar 2026 23:29:25 +0000 Subject: [PATCH 06/11] Address review feedback. --- src/librustdoc/clean/inline.rs | 4 +++- src/librustdoc/clean/mod.rs | 6 +++-- src/librustdoc/html/render/sidebar.rs | 7 +++--- .../deref/negative-deref-impl-128801.rs | 23 +++++++++++++++++++ 4 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 tests/rustdoc-html/deref/negative-deref-impl-128801.rs diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 09b2bc5dcef1d..e6a1b7af19f90 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -583,7 +583,9 @@ pub(crate) fn build_impl( }; let trait_ = associated_trait .map(|t| clean_trait_ref_with_constraints(cx, ty::Binder::dummy(t), ThinVec::new())); - if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() { + if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() + && polarity != ty::ImplPolarity::Negative + { super::build_deref_target_impls(cx, &trait_items, ret); } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index f54339429fa58..27a1236e18bd9 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2941,9 +2941,11 @@ fn clean_impl<'tcx>( .map(|&ii| clean_impl_item(tcx.hir_impl_item(ii), cx)) .collect::>(); - // If this impl block is an implementation of the Deref trait, then we + // If this impl block is a positive implementation of the Deref trait, then we // need to try inlining the target's inherent impl blocks as well. - if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() { + if trait_.as_ref().is_some_and(|t| tcx.lang_items().deref_trait() == Some(t.def_id())) + && tcx.impl_polarity(def_id) != ty::ImplPolarity::Negative + { build_deref_target_impls(cx, &items, &mut ret); } diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index a4535792ac3ce..472136ac46bcc 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -466,9 +466,9 @@ fn sidebar_assoc_items<'a>( ]; if v.iter().any(|i| i.inner_impl().trait_.is_some()) { - if let Some(impl_) = - v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait()) - { + if let Some(impl_) = v.iter().find(|i| { + i.trait_did() == cx.tcx().lang_items().deref_trait() && !i.is_negative_trait_impl() + }) { let mut derefs = DefIdSet::default(); derefs.insert(did); sidebar_deref_methods( @@ -579,6 +579,7 @@ fn sidebar_deref_methods<'a>( .as_ref() .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait()) .unwrap_or(false) + && !i.is_negative_trait_impl() }) { sidebar_deref_methods( diff --git a/tests/rustdoc-html/deref/negative-deref-impl-128801.rs b/tests/rustdoc-html/deref/negative-deref-impl-128801.rs new file mode 100644 index 0000000000000..51be958c33bbb --- /dev/null +++ b/tests/rustdoc-html/deref/negative-deref-impl-128801.rs @@ -0,0 +1,23 @@ +#![feature(negative_impls)] +#![crate_name = "foo"] + +// Regression test for https://github.com/rust-lang/rust/issues/128801 +// Negative `Deref`/`DerefMut` impls should not cause an ICE and should still be rendered. + +pub struct Source; + +//@ has foo/struct.Source.html + +// Verify negative Deref impl is rendered in the main content. +//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'impl !Deref for Source' + +// Verify negative DerefMut impl is rendered in the main content. +//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'impl !DerefMut for Source' + +// Verify negative impls appear in the sidebar. +//@ has - '//div[@class="sidebar-elems"]//h3/a[@href="#trait-implementations"]' 'Trait Implementations' +//@ has - '//*[@class="sidebar-elems"]//section//a' '!Deref' +//@ has - '//*[@class="sidebar-elems"]//section//a' '!DerefMut' + +impl !std::ops::Deref for Source {} +impl !std::ops::DerefMut for Source {} From 3bde2a716586e0c8dadcf75feaff1a2725c75ab2 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Sun, 29 Mar 2026 10:32:11 +0000 Subject: [PATCH 07/11] allow attribute parser to generate suggestions themselves --- .../src/attributes/autodiff.rs | 26 +- .../rustc_attr_parsing/src/attributes/cfg.rs | 29 +- .../src/attributes/cfi_encoding.rs | 7 +- .../src/attributes/codegen_attrs.rs | 89 +++-- .../src/attributes/confusables.rs | 5 +- .../src/attributes/crate_level.rs | 56 ++- .../src/attributes/debugger.rs | 13 +- .../src/attributes/deprecation.rs | 14 +- .../rustc_attr_parsing/src/attributes/doc.rs | 8 +- .../src/attributes/inline.rs | 12 +- .../src/attributes/instruction_set.rs | 14 +- .../src/attributes/link_attrs.rs | 69 ++-- .../src/attributes/macro_attrs.rs | 28 +- .../rustc_attr_parsing/src/attributes/mod.rs | 2 +- .../src/attributes/must_not_suspend.rs | 4 +- .../src/attributes/must_use.rs | 4 +- .../rustc_attr_parsing/src/attributes/path.rs | 5 +- .../src/attributes/proc_macro_attrs.rs | 29 +- .../src/attributes/prototype.rs | 22 +- .../rustc_attr_parsing/src/attributes/repr.rs | 11 +- .../src/attributes/rustc_allocator.rs | 3 +- .../src/attributes/rustc_internal.rs | 147 +++---- .../src/attributes/stability.rs | 29 +- .../src/attributes/test_attrs.rs | 47 ++- .../src/attributes/traits.rs | 14 +- .../src/attributes/transparency.rs | 5 +- .../rustc_attr_parsing/src/attributes/util.rs | 13 +- compiler/rustc_attr_parsing/src/context.rs | 362 +++++++++++------- compiler/rustc_attr_parsing/src/parser.rs | 2 +- .../src/session_diagnostics.rs | 66 +++- 30 files changed, 671 insertions(+), 464 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/autodiff.rs b/compiler/rustc_attr_parsing/src/attributes/autodiff.rs index edd9d9c196cf3..31e79e118bfaa 100644 --- a/compiler/rustc_attr_parsing/src/attributes/autodiff.rs +++ b/compiler/rustc_attr_parsing/src/attributes/autodiff.rs @@ -36,7 +36,8 @@ impl SingleAttributeParser for RustcAutodiffParser { ArgParser::NoArgs => return Some(AttributeKind::RustcAutodiff(None)), ArgParser::List(list) => list, ArgParser::NameValue(_) => { - cx.expected_list_or_no_args(cx.attr_span); + let attr_span = cx.attr_span; + cx.adcx().expected_list_or_no_args(attr_span); return None; } }; @@ -45,23 +46,23 @@ impl SingleAttributeParser for RustcAutodiffParser { // Parse name let Some(mode) = items.next() else { - cx.expected_at_least_one_argument(list.span); + cx.adcx().expected_at_least_one_argument(list.span); return None; }; let Some(mode) = mode.meta_item() else { - cx.expected_identifier(mode.span()); + cx.adcx().expected_identifier(mode.span()); return None; }; let Ok(()) = mode.args().no_args() else { - cx.expected_identifier(mode.span()); + cx.adcx().expected_identifier(mode.span()); return None; }; let Some(mode) = mode.path().word() else { - cx.expected_identifier(mode.span()); + cx.adcx().expected_identifier(mode.span()); return None; }; let Ok(mode) = DiffMode::from_str(mode.as_str()) else { - cx.expected_specific_argument(mode.span, DiffMode::all_modes()); + cx.adcx().expected_specific_argument(mode.span, DiffMode::all_modes()); return None; }; @@ -81,26 +82,29 @@ impl SingleAttributeParser for RustcAutodiffParser { let mut activities = ThinVec::new(); for activity in items { let MetaItemOrLitParser::MetaItemParser(activity) = activity else { - cx.expected_specific_argument(activity.span(), DiffActivity::all_activities()); + cx.adcx() + .expected_specific_argument(activity.span(), DiffActivity::all_activities()); return None; }; let Ok(()) = activity.args().no_args() else { - cx.expected_specific_argument(activity.span(), DiffActivity::all_activities()); + cx.adcx() + .expected_specific_argument(activity.span(), DiffActivity::all_activities()); return None; }; let Some(activity) = activity.path().word() else { - cx.expected_specific_argument(activity.span(), DiffActivity::all_activities()); + cx.adcx() + .expected_specific_argument(activity.span(), DiffActivity::all_activities()); return None; }; let Ok(activity) = DiffActivity::from_str(activity.as_str()) else { - cx.expected_specific_argument(activity.span, DiffActivity::all_activities()); + cx.adcx().expected_specific_argument(activity.span, DiffActivity::all_activities()); return None; }; activities.push(activity); } let Some(ret_activity) = activities.pop() else { - cx.expected_specific_argument( + cx.adcx().expected_specific_argument( list.span.with_lo(list.span.hi()), DiffActivity::all_activities(), ); diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index badf696606e94..c3b693425a52b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -44,11 +44,12 @@ pub fn parse_cfg( args: &ArgParser, ) -> Option { let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; let Some(single) = list.single() else { - cx.expected_single_argument(list.span); + cx.adcx().expected_single_argument(list.span); return None; }; parse_cfg_entry(cx, single).ok() @@ -63,7 +64,7 @@ pub fn parse_cfg_entry( ArgParser::List(list) => match meta.path().word_sym() { Some(sym::not) => { let Some(single) = list.single() else { - return Err(cx.expected_single_argument(list.span)); + return Err(cx.adcx().expected_single_argument(list.span)); }; CfgEntry::Not(Box::new(parse_cfg_entry(cx, single)?), list.span) } @@ -87,14 +88,14 @@ pub fn parse_cfg_entry( a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => { let Some(name) = meta.path().word_sym().filter(|s| !s.is_path_segment_keyword()) else { - return Err(cx.expected_identifier(meta.path().span())); + return Err(cx.adcx().expected_identifier(meta.path().span())); }; parse_name_value(name, meta.path().span(), a.name_value(), meta.span(), cx)? } }, MetaItemOrLitParser::Lit(lit) => match lit.kind { LitKind::Bool(b) => CfgEntry::Bool(b, lit.span), - _ => return Err(cx.expected_identifier(lit.span)), + _ => return Err(cx.adcx().expected_identifier(lit.span)), }, }) } @@ -152,17 +153,17 @@ fn parse_cfg_entry_target( for sub_item in list.mixed() { // First, validate that this is a NameValue item let Some(sub_item) = sub_item.meta_item() else { - cx.expected_name_value(sub_item.span(), None); + cx.adcx().expected_name_value(sub_item.span(), None); continue; }; let Some(nv) = sub_item.args().name_value() else { - cx.expected_name_value(sub_item.span(), None); + cx.adcx().expected_name_value(sub_item.span(), None); continue; }; // Then, parse it as a name-value item let Some(name) = sub_item.path().word_sym().filter(|s| !s.is_path_segment_keyword()) else { - return Err(cx.expected_identifier(sub_item.path().span())); + return Err(cx.adcx().expected_identifier(sub_item.path().span())); }; let name = Symbol::intern(&format!("target_{name}")); if let Ok(cfg) = @@ -187,9 +188,9 @@ pub(crate) fn parse_name_value( None => None, Some(value) => { let Some(value_str) = value.value_as_str() else { - return Err( - cx.expected_string_literal(value.value_span, Some(value.value_as_lit())) - ); + return Err(cx + .adcx() + .expected_string_literal(value.value_span, Some(value.value_as_lit()))); }; Some((value_str, value.value_span)) } @@ -335,8 +336,10 @@ pub fn parse_cfg_attr( path: AttrPath::from_ast(&cfg_attr.get_normal_item().path, identity), description: ParsedDescription::Attribute, reason, - suggestions: CFG_ATTR_TEMPLATE - .suggestions(AttrSuggestionStyle::Attribute(cfg_attr.style), sym::cfg_attr), + suggestions: session_diagnostics::AttributeParseErrorSuggestions::CreatedByTemplate( + CFG_ATTR_TEMPLATE + .suggestions(AttrSuggestionStyle::Attribute(cfg_attr.style), sym::cfg_attr), + ), }); } } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs b/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs index 339697649164e..81d5c8f99f45f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs @@ -13,17 +13,18 @@ impl SingleAttributeParser for CfiEncodingParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(name_value) = args.name_value() else { - cx.expected_name_value(cx.attr_span, Some(sym::cfi_encoding)); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, Some(sym::cfi_encoding)); return None; }; let Some(value_str) = name_value.value_as_str() else { - cx.expected_string_literal(name_value.value_span, None); + cx.adcx().expected_string_literal(name_value.value_span, None); return None; }; if value_str.as_str().trim().is_empty() { - cx.expected_non_empty_string_literal(name_value.value_span); + cx.adcx().expected_non_empty_string_literal(name_value.value_span); return None; } diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index dbf289ebccea7..ff94cf50adf67 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -24,12 +24,13 @@ impl SingleAttributeParser for OptimizeParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; let Some(single) = list.single() else { - cx.expected_single_argument(list.span); + cx.adcx().expected_single_argument(list.span); return None; }; @@ -38,7 +39,8 @@ impl SingleAttributeParser for OptimizeParser { Some(sym::speed) => OptimizeAttr::Speed, Some(sym::none) => OptimizeAttr::DoNotOptimize, _ => { - cx.expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]); + cx.adcx() + .expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]); OptimizeAttr::Default } }; @@ -83,17 +85,18 @@ impl SingleAttributeParser for CoverageParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(args) = args.list() else { - cx.expected_specific_argument_and_list(cx.attr_span, &[sym::on, sym::off]); + let attr_span = cx.attr_span; + cx.adcx().expected_specific_argument_and_list(attr_span, &[sym::on, sym::off]); return None; }; let Some(arg) = args.single() else { - cx.expected_single_argument(args.span); + cx.adcx().expected_single_argument(args.span); return None; }; - let fail_incorrect_argument = - |span| cx.expected_specific_argument(span, &[sym::on, sym::off]); + let mut fail_incorrect_argument = + |span| cx.adcx().expected_specific_argument(span, &[sym::on, sym::off]); let Some(arg) = arg.meta_item() else { fail_incorrect_argument(args.span); @@ -133,11 +136,12 @@ impl SingleAttributeParser for ExportNameParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; let Some(name) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; if name.as_str().contains('\0') { @@ -161,7 +165,8 @@ impl SingleAttributeParser for RustcObjcClassParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; let Some(classname) = nv.value_as_str() else { @@ -192,7 +197,8 @@ impl SingleAttributeParser for RustcObjcSelectorParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; let Some(methname) = nv.value_as_str() else { @@ -221,7 +227,7 @@ impl AttributeParser for NakedParser { const ATTRIBUTES: AcceptMapping = &[(&[sym::naked], template!(Word), |this, cx, args| { if let Err(span) = args.no_args() { - cx.expected_no_args(span); + cx.adcx().expected_no_args(span); return; } @@ -383,7 +389,7 @@ impl AttributeParser for UsedParser { ArgParser::NoArgs => UsedBy::Default, ArgParser::List(list) => { let Some(l) = list.single() else { - cx.expected_single_argument(list.span); + cx.adcx().expected_single_argument(list.span); return; }; @@ -413,7 +419,10 @@ impl AttributeParser for UsedParser { UsedBy::Linker } _ => { - cx.expected_specific_argument(l.span(), &[sym::compiler, sym::linker]); + cx.adcx().expected_specific_argument( + l.span(), + &[sym::compiler, sym::linker], + ); return; } } @@ -472,36 +481,39 @@ fn parse_tf_attribute( ) -> impl IntoIterator { let mut features = Vec::new(); let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return features; }; if list.is_empty() { - cx.warn_empty_attribute(cx.attr_span); + let attr_span = cx.attr_span; + cx.adcx().warn_empty_attribute(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)); + cx.adcx().expected_name_value(item.span(), Some(sym::enable)); return features; }; // Validate name let Some(name) = name_value.path().word_sym() else { - cx.expected_name_value(name_value.path().span(), Some(sym::enable)); + cx.adcx().expected_name_value(name_value.path().span(), Some(sym::enable)); return features; }; if name != sym::enable { - cx.expected_name_value(name_value.path().span(), Some(sym::enable)); + cx.adcx().expected_name_value(name_value.path().span(), Some(sym::enable)); return features; } // Use value let Some(name_value) = name_value.args().name_value() else { - cx.expected_name_value(item.span(), Some(sym::enable)); + cx.adcx().expected_name_value(item.span(), Some(sym::enable)); return features; }; let Some(value_str) = name_value.value_as_str() else { - cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit())); + cx.adcx() + .expected_string_literal(name_value.value_span, Some(name_value.value_as_lit())); return features; }; for feature in value_str.as_str().split(",") { @@ -595,7 +607,8 @@ impl SingleAttributeParser for SanitizeParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; @@ -605,13 +618,13 @@ impl SingleAttributeParser for SanitizeParser { for item in list.mixed() { let Some(item) = item.meta_item() else { - cx.expected_name_value(item.span(), None); + cx.adcx().expected_name_value(item.span(), None); continue; }; let path = item.path().word_sym(); let Some(value) = item.args().name_value() else { - cx.expected_name_value(item.span(), path); + cx.adcx().expected_name_value(item.span(), path); continue; }; @@ -620,14 +633,15 @@ impl SingleAttributeParser for SanitizeParser { Some(sym::on) => true, Some(sym::off) => false, Some(_) => { - cx.expected_specific_argument_strings( + cx.adcx().expected_specific_argument_strings( value.value_span, &[sym::on, sym::off], ); return; } None => { - cx.expected_string_literal(value.value_span, Some(value.value_as_lit())); + cx.adcx() + .expected_string_literal(value.value_span, Some(value.value_as_lit())); return; } }; @@ -657,14 +671,14 @@ impl SingleAttributeParser for SanitizeParser { Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking), Some(sym::caller) => rtsan = Some(RtsanSetting::Caller), _ => { - cx.expected_specific_argument_strings( + cx.adcx().expected_specific_argument_strings( value.value_span, &[sym::nonblocking, sym::blocking, sym::caller], ); } }, _ => { - cx.expected_specific_argument_strings( + cx.adcx().expected_specific_argument_strings( item.path().span(), &[ sym::address, @@ -727,7 +741,8 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(meta_item_list) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; @@ -735,7 +750,7 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { let mut entry = None; if meta_item_list.len() == 0 { - cx.expected_list(meta_item_list.span, args); + cx.adcx().expected_list(meta_item_list.span, args); return None; } @@ -744,13 +759,13 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { for item in meta_item_list.mixed() { let Some(meta_item) = item.meta_item() else { errored = true; - cx.expected_name_value(item.span(), None); + cx.adcx().expected_name_value(item.span(), None); continue; }; let Some(name_value_lit) = meta_item.args().name_value() else { errored = true; - cx.expected_name_value(item.span(), None); + cx.adcx().expected_name_value(item.span(), None); continue; }; @@ -759,7 +774,7 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { // Duplicate prefixes are not allowed if prefix.is_some() { errored = true; - cx.duplicate_key(meta_item.path().span(), sym::prefix_nops); + cx.adcx().duplicate_key(meta_item.path().span(), sym::prefix_nops); continue; } &mut prefix @@ -768,14 +783,14 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { // Duplicate entries are not allowed if entry.is_some() { errored = true; - cx.duplicate_key(meta_item.path().span(), sym::entry_nops); + cx.adcx().duplicate_key(meta_item.path().span(), sym::entry_nops); continue; } &mut entry } _ => { errored = true; - cx.expected_specific_argument( + cx.adcx().expected_specific_argument( meta_item.path().span(), &[sym::prefix_nops, sym::entry_nops], ); @@ -785,13 +800,13 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { let rustc_ast::LitKind::Int(val, _) = name_value_lit.value_as_lit().kind else { errored = true; - cx.expected_integer_literal(name_value_lit.value_span); + cx.adcx().expected_integer_literal(name_value_lit.value_span); continue; }; let Ok(val) = val.get().try_into() else { errored = true; - cx.expected_integer_literal_in_range( + cx.adcx().expected_integer_literal_in_range( name_value_lit.value_span, u8::MIN as isize, u8::MAX as isize, diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs index 1897fed1e250f..e4634547358c9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs +++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs @@ -13,7 +13,8 @@ impl AttributeParser for ConfusablesParser { template!(List: &[r#""name1", "name2", ..."#]), |this, cx, args| { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return; }; @@ -25,7 +26,7 @@ impl AttributeParser for ConfusablesParser { let span = param.span(); let Some(lit) = param.lit().and_then(|i| i.value_str()) else { - cx.expected_string_literal(span, param.lit()); + cx.adcx().expected_string_literal(span, param.lit()); continue; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 72a0945a4199d..774fd0805a7a8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -16,12 +16,13 @@ impl SingleAttributeParser for CrateNameParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(n) = args else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; let Some(name) = n.value_as_str() else { - cx.expected_string_literal(n.value_span, Some(n.value_as_lit())); + cx.adcx().expected_string_literal(n.value_span, Some(n.value_as_lit())); return None; }; @@ -46,12 +47,13 @@ impl CombineAttributeParser for CrateTypeParser { args: &ArgParser, ) -> impl IntoIterator { let ArgParser::NameValue(n) = args else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; let Some(crate_type) = n.value_as_str() else { - cx.expected_string_literal(n.value_span, Some(n.value_as_lit())); + cx.adcx().expected_string_literal(n.value_span, Some(n.value_as_lit())); return None; }; @@ -89,7 +91,8 @@ impl SingleAttributeParser for RecursionLimitParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; @@ -111,7 +114,8 @@ impl SingleAttributeParser for MoveSizeLimitParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; @@ -133,7 +137,8 @@ impl SingleAttributeParser for TypeLengthLimitParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; @@ -155,7 +160,8 @@ impl SingleAttributeParser for PatternComplexityLimitParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; @@ -213,8 +219,9 @@ impl SingleAttributeParser for WindowsSubsystemParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { - cx.expected_name_value( - args.span().unwrap_or(cx.inner_span), + let inner_span = cx.inner_span; + cx.adcx().expected_name_value( + args.span().unwrap_or(inner_span), Some(sym::windows_subsystem), ); return None; @@ -224,7 +231,10 @@ impl SingleAttributeParser for WindowsSubsystemParser { Some(sym::console) => WindowsSubsystemKind::Console, Some(sym::windows) => WindowsSubsystemKind::Windows, Some(_) | None => { - cx.expected_specific_argument_strings(nv.value_span, &[sym::console, sym::windows]); + cx.adcx().expected_specific_argument_strings( + nv.value_span, + &[sym::console, sym::windows], + ); return None; } }; @@ -310,29 +320,31 @@ impl CombineAttributeParser for FeatureParser { args: &ArgParser, ) -> impl IntoIterator { let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return Vec::new(); }; if list.is_empty() { - cx.warn_empty_attribute(cx.attr_span); + let attr_span = cx.attr_span; + cx.adcx().warn_empty_attribute(attr_span); } let mut res = Vec::new(); for elem in list.mixed() { let Some(elem) = elem.meta_item() else { - cx.expected_identifier(elem.span()); + cx.adcx().expected_identifier(elem.span()); continue; }; if let Err(arg_span) = elem.args().no_args() { - cx.expected_no_args(arg_span); + cx.adcx().expected_no_args(arg_span); continue; } let path = elem.path(); let Some(ident) = path.word() else { - cx.expected_identifier(path.span()); + cx.adcx().expected_identifier(path.span()); continue; }; res.push(ident); @@ -356,29 +368,31 @@ impl CombineAttributeParser for RegisterToolParser { args: &ArgParser, ) -> impl IntoIterator { let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return Vec::new(); }; if list.is_empty() { - cx.warn_empty_attribute(cx.attr_span); + let attr_span = cx.attr_span; + cx.adcx().warn_empty_attribute(attr_span); } let mut res = Vec::new(); for elem in list.mixed() { let Some(elem) = elem.meta_item() else { - cx.expected_identifier(elem.span()); + cx.adcx().expected_identifier(elem.span()); continue; }; if let Err(arg_span) = elem.args().no_args() { - cx.expected_no_args(arg_span); + cx.adcx().expected_no_args(arg_span); continue; } let path = elem.path(); let Some(ident) = path.word() else { - cx.expected_identifier(path.span()); + cx.adcx().expected_identifier(path.span()); continue; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/debugger.rs b/compiler/rustc_attr_parsing/src/attributes/debugger.rs index 52a66942cf939..3bde4949bce04 100644 --- a/compiler/rustc_attr_parsing/src/attributes/debugger.rs +++ b/compiler/rustc_attr_parsing/src/attributes/debugger.rs @@ -21,15 +21,16 @@ impl CombineAttributeParser for DebuggerViualizerParser { args: &ArgParser, ) -> impl IntoIterator { let Some(l) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; let Some(single) = l.single() else { - cx.expected_single_argument(l.span); + cx.adcx().expected_single_argument(l.span); return None; }; let Some(mi) = single.meta_item() else { - cx.expected_name_value(single.span(), None); + cx.adcx().expected_name_value(single.span(), None); return None; }; let path = mi.path().word_sym(); @@ -37,7 +38,7 @@ impl CombineAttributeParser for DebuggerViualizerParser { Some(sym::natvis_file) => DebuggerVisualizerType::Natvis, Some(sym::gdb_script_file) => DebuggerVisualizerType::GdbPrettyPrinter, _ => { - cx.expected_specific_argument( + cx.adcx().expected_specific_argument( mi.path().span(), &[sym::natvis_file, sym::gdb_script_file], ); @@ -46,12 +47,12 @@ impl CombineAttributeParser for DebuggerViualizerParser { }; let Some(path) = mi.args().name_value() else { - cx.expected_name_value(single.span(), path); + cx.adcx().expected_name_value(single.span(), path); return None; }; let Some(path) = path.value_as_str() else { - cx.expected_string_literal(path.value_span, Some(path.value_as_lit())); + cx.adcx().expected_string_literal(path.value_span, Some(path.value_as_lit())); return None; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 804b54e9ee256..e9281179a8189 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -8,25 +8,25 @@ use crate::session_diagnostics::{ }; fn get( - cx: &AcceptContext<'_, '_, S>, + cx: &mut AcceptContext<'_, '_, S>, name: Symbol, param_span: Span, arg: &ArgParser, item: Option, ) -> Option { if item.is_some() { - cx.duplicate_key(param_span, name); + cx.adcx().duplicate_key(param_span, name); return None; } if let Some(v) = arg.name_value() { if let Some(value_str) = v.value_as_ident() { Some(value_str) } else { - cx.expected_string_literal(v.value_span, Some(&v.value_as_lit())); + cx.adcx().expected_string_literal(v.value_span, Some(&v.value_as_lit())); None } } else { - cx.expected_name_value(param_span, Some(name)); + cx.adcx().expected_name_value(param_span, Some(name)); None } } @@ -83,7 +83,7 @@ impl SingleAttributeParser for DeprecatedParser { ArgParser::List(list) => { for param in list.mixed() { let Some(param) = param.meta_item() else { - cx.unexpected_literal(param.span()); + cx.adcx().unexpected_literal(param.span()); return None; }; @@ -115,7 +115,7 @@ impl SingleAttributeParser for DeprecatedParser { Some(get(cx, name, param.span(), param.args(), suggestion)?.name); } _ => { - cx.expected_specific_argument( + cx.adcx().expected_specific_argument( param.span(), if features.deprecated_suggestion() { &[sym::since, sym::note, sym::suggestion] @@ -130,7 +130,7 @@ impl SingleAttributeParser for DeprecatedParser { } ArgParser::NameValue(v) => { let Some(value) = v.value_as_ident() else { - cx.expected_string_literal(v.value_span, Some(v.value_as_lit())); + cx.adcx().expected_string_literal(v.value_span, Some(v.value_as_lit())); return None; }; note = Some(value); diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 099a75e11f3af..a569483ea7c39 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -136,7 +136,7 @@ fn parse_keyword_and_attribute( let span = path.span(); if attr_value.is_some() { - cx.duplicate_key(span, path.word_sym().unwrap()); + cx.adcx().duplicate_key(span, path.word_sym().unwrap()); return; } @@ -275,7 +275,7 @@ impl DocParser { ArgParser::List(list) => { for i in list.mixed() { let Some(alias) = i.lit().and_then(|i| i.value_str()) else { - cx.expected_string_literal(i.span(), i.lit()); + cx.adcx().expected_string_literal(i.span(), i.lit()); continue; }; @@ -284,7 +284,7 @@ impl DocParser { } ArgParser::NameValue(nv) => { let Some(alias) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return; }; self.add_alias(cx, alias, nv.value_span); @@ -661,7 +661,7 @@ impl DocParser { ) { match args { ArgParser::NoArgs => { - let suggestions = cx.suggestions(); + let suggestions = cx.adcx().suggestions(); let span = cx.attr_span; cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index 82cec25c997ca..f34a776d9ae88 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -38,7 +38,7 @@ impl SingleAttributeParser for InlineParser { 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); + cx.adcx().expected_single_argument(list.span); return None; }; @@ -50,13 +50,13 @@ impl SingleAttributeParser for InlineParser { Some(AttributeKind::Inline(InlineAttr::Never, cx.attr_span)) } _ => { - cx.expected_specific_argument(l.span(), &[sym::always, sym::never]); + cx.adcx().expected_specific_argument(l.span(), &[sym::always, sym::never]); return None; } } } ArgParser::NameValue(_) => { - cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); + cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); return None; } } @@ -80,12 +80,12 @@ impl SingleAttributeParser for RustcForceInlineParser { ArgParser::NoArgs => None, ArgParser::List(list) => { let Some(l) = list.single() else { - cx.expected_single_argument(list.span); + cx.adcx().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(), l.lit()); + cx.adcx().expected_string_literal(l.span(), l.lit()); return None; }; @@ -93,7 +93,7 @@ impl SingleAttributeParser for RustcForceInlineParser { } ArgParser::NameValue(v) => { let Some(reason) = v.value_as_str() else { - cx.expected_string_literal(v.value_span, Some(v.value_as_lit())); + cx.adcx().expected_string_literal(v.value_span, Some(v.value_as_lit())); return None; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/instruction_set.rs b/compiler/rustc_attr_parsing/src/attributes/instruction_set.rs index 5f6108108a774..00e2241f2e701 100644 --- a/compiler/rustc_attr_parsing/src/attributes/instruction_set.rs +++ b/compiler/rustc_attr_parsing/src/attributes/instruction_set.rs @@ -21,24 +21,25 @@ impl SingleAttributeParser for InstructionSetParser { const POSSIBLE_SYMBOLS: &[Symbol] = &[sym::arm_a32, sym::arm_t32]; const POSSIBLE_ARM_SYMBOLS: &[Symbol] = &[sym::a32, sym::t32]; let Some(maybe_meta_item) = args.list().and_then(MetaItemListParser::single) else { - cx.expected_specific_argument(cx.attr_span, POSSIBLE_SYMBOLS); + let attr_span = cx.attr_span; + cx.adcx().expected_specific_argument(attr_span, POSSIBLE_SYMBOLS); return None; }; let Some(meta_item) = maybe_meta_item.meta_item() else { - cx.expected_specific_argument(maybe_meta_item.span(), POSSIBLE_SYMBOLS); + cx.adcx().expected_specific_argument(maybe_meta_item.span(), POSSIBLE_SYMBOLS); return None; }; let mut segments = meta_item.path().segments(); let Some(architecture) = segments.next() else { - cx.expected_specific_argument(meta_item.span(), POSSIBLE_SYMBOLS); + cx.adcx().expected_specific_argument(meta_item.span(), POSSIBLE_SYMBOLS); return None; }; let Some(instruction_set) = segments.next() else { - cx.expected_specific_argument(architecture.span, POSSIBLE_SYMBOLS); + cx.adcx().expected_specific_argument(architecture.span, POSSIBLE_SYMBOLS); return None; }; @@ -56,13 +57,14 @@ impl SingleAttributeParser for InstructionSetParser { sym::a32 => InstructionSetAttr::ArmA32, sym::t32 => InstructionSetAttr::ArmT32, _ => { - cx.expected_specific_argument(instruction_set.span, POSSIBLE_ARM_SYMBOLS); + cx.adcx() + .expected_specific_argument(instruction_set.span, POSSIBLE_ARM_SYMBOLS); return None; } } } _ => { - cx.expected_specific_argument(architecture.span, POSSIBLE_SYMBOLS); + cx.adcx().expected_specific_argument(architecture.span, POSSIBLE_SYMBOLS); return None; } }; diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 52ab4ac8a4494..e1ae2068f9fd3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -34,11 +34,12 @@ impl SingleAttributeParser for LinkNameParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; let Some(name) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; @@ -71,11 +72,12 @@ impl CombineAttributeParser for LinkParser { // Specifically `#[link = "dl"]` is accepted with a FCW // For more information, see https://github.com/rust-lang/rust/pull/143193 ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => { - cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); + cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); return None; } _ => { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; } }; @@ -91,7 +93,7 @@ impl CombineAttributeParser for LinkParser { let mut import_name_type = None; for item in items.mixed() { let Some(item) = item.meta_item() else { - cx.unexpected_literal(item.span()); + cx.adcx().unexpected_literal(item.span()); continue; }; @@ -107,7 +109,7 @@ impl CombineAttributeParser for LinkParser { Self::parse_link_import_name_type(item, &mut import_name_type, cx) } _ => { - cx.expected_specific_argument_strings( + cx.adcx().expected_specific_argument_strings( item.span(), &[ sym::name, @@ -193,7 +195,7 @@ impl CombineAttributeParser for LinkParser { } _ => { - cx.expected_specific_argument_strings( + cx.adcx().expected_specific_argument_strings( span, &[ sym::bundle, @@ -253,15 +255,15 @@ impl LinkParser { cx: &mut AcceptContext<'_, '_, S>, ) -> bool { if name.is_some() { - cx.duplicate_key(item.span(), sym::name); + cx.adcx().duplicate_key(item.span(), sym::name); return true; } let Some(nv) = item.args().name_value() else { - cx.expected_name_value(item.span(), Some(sym::name)); + cx.adcx().expected_name_value(item.span(), Some(sym::name)); return false; }; let Some(link_name) = nv.value_as_str() else { - cx.expected_name_value(item.span(), Some(sym::name)); + cx.adcx().expected_name_value(item.span(), Some(sym::name)); return false; }; @@ -280,15 +282,15 @@ impl LinkParser { features: &Features, ) -> bool { if kind.is_some() { - cx.duplicate_key(item.span(), sym::kind); + cx.adcx().duplicate_key(item.span(), sym::kind); return true; } let Some(nv) = item.args().name_value() else { - cx.expected_name_value(item.span(), Some(sym::kind)); + cx.adcx().expected_name_value(item.span(), Some(sym::kind)); return true; }; let Some(link_kind) = nv.value_as_str() else { - cx.expected_name_value(item.span(), Some(sym::kind)); + cx.adcx().expected_name_value(item.span(), Some(sym::kind)); return true; }; @@ -337,7 +339,7 @@ impl LinkParser { NativeLibKind::LinkArg } _kind => { - cx.expected_specific_argument_strings( + cx.adcx().expected_specific_argument_strings( nv.value_span, &[ kw::Static, @@ -360,15 +362,15 @@ impl LinkParser { cx: &mut AcceptContext<'_, '_, S>, ) -> bool { if modifiers.is_some() { - cx.duplicate_key(item.span(), sym::modifiers); + cx.adcx().duplicate_key(item.span(), sym::modifiers); return true; } let Some(nv) = item.args().name_value() else { - cx.expected_name_value(item.span(), Some(sym::modifiers)); + cx.adcx().expected_name_value(item.span(), Some(sym::modifiers)); return true; }; let Some(link_modifiers) = nv.value_as_str() else { - cx.expected_name_value(item.span(), Some(sym::modifiers)); + cx.adcx().expected_name_value(item.span(), Some(sym::modifiers)); return true; }; *modifiers = Some((link_modifiers, nv.value_span)); @@ -383,15 +385,15 @@ impl LinkParser { features: &Features, ) -> bool { if cfg.is_some() { - cx.duplicate_key(item.span(), sym::cfg); + cx.adcx().duplicate_key(item.span(), sym::cfg); return true; } let Some(link_cfg) = item.args().list() else { - cx.expected_list(item.span(), item.args()); + cx.adcx().expected_list(item.span(), item.args()); return true; }; let Some(link_cfg) = link_cfg.single() else { - cx.expected_single_argument(item.span()); + cx.adcx().expected_single_argument(item.span()); return true; }; if !features.link_cfg() { @@ -407,15 +409,15 @@ impl LinkParser { cx: &mut AcceptContext<'_, '_, S>, ) -> bool { if wasm_import_module.is_some() { - cx.duplicate_key(item.span(), sym::wasm_import_module); + cx.adcx().duplicate_key(item.span(), sym::wasm_import_module); return true; } let Some(nv) = item.args().name_value() else { - cx.expected_name_value(item.span(), Some(sym::wasm_import_module)); + cx.adcx().expected_name_value(item.span(), Some(sym::wasm_import_module)); return true; }; let Some(link_wasm_import_module) = nv.value_as_str() else { - cx.expected_name_value(item.span(), Some(sym::wasm_import_module)); + cx.adcx().expected_name_value(item.span(), Some(sym::wasm_import_module)); return true; }; *wasm_import_module = Some((link_wasm_import_module, item.span())); @@ -428,15 +430,15 @@ impl LinkParser { cx: &mut AcceptContext<'_, '_, S>, ) -> bool { if import_name_type.is_some() { - cx.duplicate_key(item.span(), sym::import_name_type); + cx.adcx().duplicate_key(item.span(), sym::import_name_type); return true; } let Some(nv) = item.args().name_value() else { - cx.expected_name_value(item.span(), Some(sym::import_name_type)); + cx.adcx().expected_name_value(item.span(), Some(sym::import_name_type)); return true; }; let Some(link_import_name_type) = nv.value_as_str() else { - cx.expected_name_value(item.span(), Some(sym::import_name_type)); + cx.adcx().expected_name_value(item.span(), Some(sym::import_name_type)); return true; }; if cx.sess().target.arch != Arch::X86 { @@ -449,7 +451,7 @@ impl LinkParser { sym::noprefix => PeImportNameType::NoPrefix, sym::undecorated => PeImportNameType::Undecorated, _ => { - cx.expected_specific_argument_strings( + cx.adcx().expected_specific_argument_strings( item.span(), &[sym::decorated, sym::noprefix, sym::undecorated], ); @@ -480,11 +482,12 @@ impl SingleAttributeParser for LinkSectionParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; let Some(name) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; if name.as_str().contains('\0') { @@ -606,12 +609,14 @@ impl SingleAttributeParser for LinkageParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(name_value) = args.name_value() else { - cx.expected_name_value(cx.attr_span, Some(sym::linkage)); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, Some(sym::linkage)); return None; }; let Some(value) = name_value.value_as_str() else { - cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit())); + cx.adcx() + .expected_string_literal(name_value.value_span, Some(name_value.value_as_lit())); return None; }; @@ -635,7 +640,7 @@ impl SingleAttributeParser for LinkageParser { sym::weak_odr => Linkage::WeakODR, _ => { - cx.expected_specific_argument( + cx.adcx().expected_specific_argument( name_value.value_span, &[ sym::available_externally, diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 86dde5b108ff2..e49ad5695c120 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -66,7 +66,7 @@ impl AttributeParser for MacroUseParser { } ArgParser::List(list) => { if list.is_empty() { - cx.warn_empty_attribute(list.span); + cx.adcx().warn_empty_attribute(list.span); return; } @@ -83,15 +83,15 @@ impl AttributeParser for MacroUseParser { for item in list.mixed() { let Some(item) = item.meta_item() else { - cx.expected_identifier(item.span()); + cx.adcx().expected_identifier(item.span()); continue; }; if let Err(err_span) = item.args().no_args() { - cx.expected_no_args(err_span); + cx.adcx().expected_no_args(err_span); continue; } let Some(item) = item.path().word() else { - cx.expected_identifier(item.span()); + cx.adcx().expected_identifier(item.span()); continue; }; arguments.push(item); @@ -100,7 +100,7 @@ impl AttributeParser for MacroUseParser { } } ArgParser::NameValue(nv) => { - cx.expected_list_or_no_args(nv.args_span()); + cx.adcx().expected_list_or_no_args(nv.args_span()); } } }, @@ -143,19 +143,19 @@ impl SingleAttributeParser for MacroExportParser { ArgParser::NoArgs => false, ArgParser::List(list) => { let Some(l) = list.single() else { - cx.warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS); + cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS); return None; }; match l.meta_item().and_then(|i| i.path().word_sym()) { Some(sym::local_inner_macros) => true, _ => { - cx.warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS); + cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS); return None; } } } ArgParser::NameValue(nv) => { - cx.expected_list_or_no_args(nv.args_span()); + cx.adcx().expected_list_or_no_args(nv.args_span()); return None; } }; @@ -176,19 +176,20 @@ impl SingleAttributeParser for CollapseDebugInfoParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; let Some(single) = list.single() else { - cx.expected_single_argument(list.span); + cx.adcx().expected_single_argument(list.span); return None; }; let Some(mi) = single.meta_item() else { - cx.unexpected_literal(single.span()); + cx.adcx().unexpected_literal(single.span()); return None; }; if let Err(err) = mi.args().no_args() { - cx.expected_no_args(err); + cx.adcx().expected_no_args(err); } let path = mi.path().word_sym(); let info = match path { @@ -196,7 +197,8 @@ impl SingleAttributeParser for CollapseDebugInfoParser { Some(sym::no) => CollapseMacroDebuginfo::No, Some(sym::external) => CollapseMacroDebuginfo::External, _ => { - cx.expected_specific_argument(mi.span(), &[sym::yes, sym::no, sym::external]); + cx.adcx() + .expected_specific_argument(mi.span(), &[sym::yes, sym::no, sym::external]); return None; } }; diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 67147642921c2..d7f64ff2319a9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -247,7 +247,7 @@ impl, S: Stage> SingleAttributeParser for Without fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { if let Err(span) = args.no_args() { - cx.expected_no_args(span); + cx.adcx().expected_no_args(span); } Some(T::CREATE(cx.attr_span)) } diff --git a/compiler/rustc_attr_parsing/src/attributes/must_not_suspend.rs b/compiler/rustc_attr_parsing/src/attributes/must_not_suspend.rs index 7f37210b8c8af..a951518129584 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_not_suspend.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_not_suspend.rs @@ -18,13 +18,13 @@ impl SingleAttributeParser for MustNotSuspendParser { ArgParser::NameValue(reason) => match reason.value_as_str() { Some(val) => Some(val), None => { - cx.expected_nv_or_no_args(reason.value_span); + cx.adcx().expected_nv_or_no_args(reason.value_span); return None; } }, ArgParser::NoArgs => None, ArgParser::List(list) => { - cx.expected_nv_or_no_args(list.span); + cx.adcx().expected_nv_or_no_args(list.span); return None; } }; diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs index f1ce810f4eaea..58ad31dd54845 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -32,7 +32,7 @@ impl SingleAttributeParser for MustUseParser { ArgParser::NoArgs => None, ArgParser::NameValue(name_value) => { let Some(value_str) = name_value.value_as_str() else { - cx.expected_string_literal( + cx.adcx().expected_string_literal( name_value.value_span, Some(&name_value.value_as_lit()), ); @@ -41,7 +41,7 @@ impl SingleAttributeParser for MustUseParser { Some(value_str) } ArgParser::List(list) => { - cx.expected_nv_or_no_args(list.span); + cx.adcx().expected_nv_or_no_args(list.span); return None; } }, diff --git a/compiler/rustc_attr_parsing/src/attributes/path.rs b/compiler/rustc_attr_parsing/src/attributes/path.rs index 6b5eee7f31bbd..08daa4aa5249b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/path.rs +++ b/compiler/rustc_attr_parsing/src/attributes/path.rs @@ -14,11 +14,12 @@ impl SingleAttributeParser for PathParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; let Some(path) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs index aca0e94cff06c..8e32a2d8e4923 100644 --- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs @@ -66,30 +66,31 @@ fn parse_derive_like( if args.no_args().is_ok() && !trait_name_mandatory { return Some((None, ThinVec::new())); } - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; let mut items = list.mixed(); // Parse the name of the trait that is derived. let Some(trait_attr) = items.next() else { - cx.expected_at_least_one_argument(list.span); + cx.adcx().expected_at_least_one_argument(list.span); return None; }; let Some(trait_attr) = trait_attr.meta_item() else { - cx.unexpected_literal(trait_attr.span()); + cx.adcx().unexpected_literal(trait_attr.span()); return None; }; let Some(trait_ident) = trait_attr.path().word() else { - cx.expected_identifier(trait_attr.path().span()); + cx.adcx().expected_identifier(trait_attr.path().span()); return None; }; if !trait_ident.name.can_be_raw() { - cx.expected_identifier(trait_ident.span); + cx.adcx().expected_identifier(trait_ident.span); return None; } if let Err(e) = trait_attr.args().no_args() { - cx.expected_no_args(e); + cx.adcx().expected_no_args(e); return None; }; @@ -97,34 +98,34 @@ fn parse_derive_like( let mut attributes = ThinVec::new(); if let Some(attrs) = items.next() { let Some(attr_list) = attrs.meta_item() else { - cx.unexpected_literal(attrs.span()); + cx.adcx().unexpected_literal(attrs.span()); return None; }; if !attr_list.path().word_is(sym::attributes) { - cx.expected_specific_argument(attrs.span(), &[sym::attributes]); + cx.adcx().expected_specific_argument(attrs.span(), &[sym::attributes]); return None; } let Some(attr_list) = attr_list.args().list() else { - cx.expected_list(attrs.span(), attr_list.args()); + cx.adcx().expected_list(attrs.span(), attr_list.args()); return None; }; // Parse item in `attributes(...)` argument for attr in attr_list.mixed() { let Some(attr) = attr.meta_item() else { - cx.expected_identifier(attr.span()); + cx.adcx().expected_identifier(attr.span()); return None; }; if let Err(e) = attr.args().no_args() { - cx.expected_no_args(e); + cx.adcx().expected_no_args(e); return None; }; let Some(ident) = attr.path().word() else { - cx.expected_identifier(attr.path().span()); + cx.adcx().expected_identifier(attr.path().span()); return None; }; if !ident.name.can_be_raw() { - cx.expected_identifier(ident.span); + cx.adcx().expected_identifier(ident.span); return None; } if rustc_feature::is_builtin_attr_name(ident.name) { @@ -140,7 +141,7 @@ fn parse_derive_like( // If anything else is specified, we should reject it if let Some(next) = items.next() { - cx.expected_no_args(next.span()); + cx.adcx().expected_no_args(next.span()); } Some((Some(trait_ident.name), attributes)) diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs index 15d9d3453738b..37d6909834465 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prototype.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs @@ -26,7 +26,8 @@ impl SingleAttributeParser for CustomMirParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; @@ -36,7 +37,7 @@ impl SingleAttributeParser for CustomMirParser { for item in list.mixed() { let Some(meta_item) = item.meta_item() else { - cx.expected_name_value(item.span(), None); + cx.adcx().expected_name_value(item.span(), None); failed = true; break; }; @@ -46,10 +47,10 @@ impl SingleAttributeParser for CustomMirParser { } else if let Some(arg) = meta_item.word_is(sym::phase) { extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed); } else if let Some(..) = meta_item.path().word() { - cx.expected_specific_argument(meta_item.span(), &[sym::dialect, sym::phase]); + cx.adcx().expected_specific_argument(meta_item.span(), &[sym::dialect, sym::phase]); failed = true; } else { - cx.expected_name_value(meta_item.span(), None); + cx.adcx().expected_name_value(meta_item.span(), None); failed = true; }; } @@ -75,19 +76,19 @@ fn extract_value( failed: &mut bool, ) { if out_val.is_some() { - cx.duplicate_key(span, key); + cx.adcx().duplicate_key(span, key); *failed = true; return; } let Some(val) = arg.name_value() else { - cx.expected_single_argument(arg.span().unwrap_or(span)); + cx.adcx().expected_single_argument(arg.span().unwrap_or(span)); *failed = true; return; }; let Some(value_sym) = val.value_as_str() else { - cx.expected_string_literal(val.value_span, Some(val.value_as_lit())); + cx.adcx().expected_string_literal(val.value_span, Some(val.value_as_lit())); *failed = true; return; }; @@ -108,7 +109,7 @@ fn parse_dialect( sym::runtime => MirDialect::Runtime, _ => { - cx.expected_specific_argument(span, &[sym::analysis, sym::built, sym::runtime]); + cx.adcx().expected_specific_argument(span, &[sym::analysis, sym::built, sym::runtime]); *failed = true; return None; } @@ -130,7 +131,10 @@ fn parse_phase( sym::optimized => MirPhase::Optimized, _ => { - cx.expected_specific_argument(span, &[sym::initial, sym::post_cleanup, sym::optimized]); + cx.adcx().expected_specific_argument( + span, + &[sym::initial, sym::post_cleanup, sym::optimized], + ); *failed = true; return None; } diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index f8ccc8594e333..ebb9d90eb152c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -33,12 +33,14 @@ impl CombineAttributeParser for ReprParser { let mut reprs = Vec::new(); let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return reprs; }; if list.is_empty() { - cx.warn_empty_attribute(cx.attr_span); + let attr_span = cx.attr_span; + cx.adcx().warn_empty_attribute(attr_span); return reprs; } @@ -290,11 +292,12 @@ impl RustcAlignParser { fn parse(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) { match args { ArgParser::NoArgs | ArgParser::NameValue(_) => { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); } ArgParser::List(list) => { let Some(align) = list.single() else { - cx.expected_single_argument(list.span); + cx.adcx().expected_single_argument(list.span); return; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs index e809ad9ed83b7..cf4f8eab32460 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs @@ -30,7 +30,8 @@ impl SingleAttributeParser for RustcAllocatorZeroedVariantParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "function"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(name) = args.name_value().and_then(NameValueParser::value_as_str) else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 15772d874507f..2ff059928d8ca 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -34,7 +34,8 @@ impl SingleAttributeParser for RustcMustImplementOneOfParser { const TEMPLATE: AttributeTemplate = template!(List: &["function1, function2, ..."]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + let span = cx.attr_span; + cx.adcx().expected_list(span, args); return None; }; @@ -43,14 +44,14 @@ impl SingleAttributeParser for RustcMustImplementOneOfParser { let inputs: Vec<_> = list.mixed().collect(); if inputs.len() < 2 { - cx.expected_list_with_num_args_or_more(2, list.span); + cx.adcx().expected_list_with_num_args_or_more(2, list.span); return None; } let mut errored = false; for argument in inputs { let Some(meta) = argument.meta_item() else { - cx.expected_identifier(argument.span()); + cx.adcx().expected_identifier(argument.span()); return None; }; @@ -138,7 +139,8 @@ impl SingleAttributeParser for RustcLegacyConstGenericsParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::List(meta_items) = args else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; @@ -153,14 +155,14 @@ impl SingleAttributeParser for RustcLegacyConstGenericsParser { { parsed_indexes.push((index.0 as usize, possible_index.span())); } else { - cx.expected_integer_literal(possible_index.span()); + cx.adcx().expected_integer_literal(possible_index.span()); errored = true; } } if errored { return None; } else if parsed_indexes.is_empty() { - cx.expected_at_least_one_argument(args.span()?); + cx.adcx().expected_at_least_one_argument(args.span()?); return None; } @@ -194,13 +196,14 @@ impl SingleAttributeParser for RustcLintOptDenyFieldAccessParser { const TEMPLATE: AttributeTemplate = template!(Word); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(arg) = args.list().and_then(MetaItemListParser::single) else { - cx.expected_single_argument(cx.attr_span); + let attr_span = cx.attr_span; + cx.adcx().expected_single_argument(attr_span); return None; }; let MetaItemOrLitParser::Lit(MetaItemLit { kind: LitKind::Str(lint_message, _), .. }) = arg else { - cx.expected_string_literal(arg.span(), arg.lit()); + cx.adcx().expected_string_literal(arg.span(), arg.lit()); return None; }; @@ -223,7 +226,8 @@ fn parse_cgu_fields( accepts_kind: bool, ) -> Option<(Symbol, Symbol, Option)> { let Some(args) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; @@ -233,7 +237,7 @@ fn parse_cgu_fields( for arg in args.mixed() { let Some(arg) = arg.meta_item() else { - cx.expected_name_value(args.span, None); + cx.adcx().expected_name_value(args.span, None); continue; }; @@ -242,7 +246,7 @@ fn parse_cgu_fields( Some(sym::module) => &mut module, Some(sym::kind) if accepts_kind => &mut kind, _ => { - cx.expected_specific_argument( + cx.adcx().expected_specific_argument( arg.path().span(), if accepts_kind { &[sym::cfg, sym::module, sym::kind] @@ -255,17 +259,17 @@ fn parse_cgu_fields( }; let Some(i) = arg.args().name_value() else { - cx.expected_name_value(arg.span(), None); + cx.adcx().expected_name_value(arg.span(), None); continue; }; let Some(str) = i.value_as_str() else { - cx.expected_string_literal(i.value_span, Some(i.value_as_lit())); + cx.adcx().expected_string_literal(i.value_span, Some(i.value_as_lit())); continue; }; if res.is_some() { - cx.duplicate_key(arg.span(), arg.ident().unwrap().name); + cx.adcx().duplicate_key(arg.span(), arg.ident().unwrap().name); continue; } @@ -287,7 +291,7 @@ fn parse_cgu_fields( sym::post_dash_lto => CguKind::PostDashLto, sym::any => CguKind::Any, _ => { - cx.expected_specific_argument_strings( + cx.adcx().expected_specific_argument_strings( span, &[sym::no, sym::pre_dash_lto, sym::post_dash_lto, sym::any], ); @@ -372,32 +376,33 @@ impl SingleAttributeParser for RustcDeprecatedSafe2024Parser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(args) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; let Some(single) = args.single() else { - cx.expected_single_argument(args.span); + cx.adcx().expected_single_argument(args.span); return None; }; let Some(arg) = single.meta_item() else { - cx.expected_name_value(args.span, None); + cx.adcx().expected_name_value(args.span, None); return None; }; let Some(args) = arg.word_is(sym::audit_that) else { - cx.expected_specific_argument(arg.span(), &[sym::audit_that]); + cx.adcx().expected_specific_argument(arg.span(), &[sym::audit_that]); return None; }; let Some(nv) = args.name_value() else { - cx.expected_name_value(arg.span(), Some(sym::audit_that)); + cx.adcx().expected_name_value(arg.span(), Some(sym::audit_that)); return None; }; let Some(suggestion) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; @@ -442,7 +447,8 @@ impl SingleAttributeParser for RustcNeverTypeOptionsParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; @@ -451,7 +457,7 @@ impl SingleAttributeParser for RustcNeverTypeOptionsParser { for arg in list.mixed() { let Some(meta) = arg.meta_item() else { - cx.expected_name_value(arg.span(), None); + cx.adcx().expected_name_value(arg.span(), None); continue; }; @@ -459,7 +465,7 @@ impl SingleAttributeParser for RustcNeverTypeOptionsParser { Some(sym::fallback) => &mut fallback, Some(sym::diverging_block_default) => &mut diverging_block_default, _ => { - cx.expected_specific_argument( + cx.adcx().expected_specific_argument( meta.path().span(), &[sym::fallback, sym::diverging_block_default], ); @@ -468,17 +474,17 @@ impl SingleAttributeParser for RustcNeverTypeOptionsParser { }; let Some(nv) = meta.args().name_value() else { - cx.expected_name_value(meta.span(), None); + cx.adcx().expected_name_value(meta.span(), None); continue; }; let Some(field) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); continue; }; if res.is_some() { - cx.duplicate_key(meta.span(), meta.ident().unwrap().name); + cx.adcx().duplicate_key(meta.span(), meta.ident().unwrap().name); continue; } @@ -491,7 +497,8 @@ impl SingleAttributeParser for RustcNeverTypeOptionsParser { Some(Ident { name: sym::never, .. }) => Some(DivergingFallbackBehavior::ToNever), Some(Ident { name: sym::no, .. }) => Some(DivergingFallbackBehavior::NoFallback), Some(Ident { span, .. }) => { - cx.expected_specific_argument_strings(span, &[sym::unit, sym::never, sym::no]); + cx.adcx() + .expected_specific_argument_strings(span, &[sym::unit, sym::never, sym::no]); return None; } }; @@ -501,7 +508,7 @@ impl SingleAttributeParser for RustcNeverTypeOptionsParser { Some(Ident { name: sym::unit, .. }) => Some(DivergingBlockBehavior::Unit), Some(Ident { name: sym::never, .. }) => Some(DivergingBlockBehavior::Never), Some(Ident { span, .. }) => { - cx.expected_specific_argument_strings(span, &[sym::unit, sym::no]); + cx.adcx().expected_specific_argument_strings(span, &[sym::unit, sym::no]); return None; } }; @@ -591,7 +598,8 @@ impl SingleAttributeParser for RustcSimdMonomorphizeLaneLimitParser fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; Some(AttributeKind::RustcSimdMonomorphizeLaneLimit(cx.parse_limit_int(nv)?)) @@ -633,11 +641,12 @@ impl SingleAttributeParser for LangParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; let Some(name) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; let Some(lang_item) = LangItem::from_name(name) else { @@ -727,18 +736,19 @@ impl CombineAttributeParser for RustcLayoutParser { args: &ArgParser, ) -> impl IntoIterator { let ArgParser::List(items) = args else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return vec![]; }; let mut result = Vec::new(); for item in items.mixed() { let Some(arg) = item.meta_item() else { - cx.unexpected_literal(item.span()); + cx.adcx().unexpected_literal(item.span()); continue; }; let Some(ident) = arg.ident() else { - cx.expected_identifier(arg.span()); + cx.adcx().expected_identifier(arg.span()); return vec![]; }; let ty = match ident.name { @@ -748,7 +758,7 @@ impl CombineAttributeParser for RustcLayoutParser { sym::homogeneous_aggregate => RustcLayoutType::HomogenousAggregate, sym::debug => RustcLayoutType::Debug, _ => { - cx.expected_specific_argument( + cx.adcx().expected_specific_argument( ident.span, &[sym::abi, sym::align, sym::size, sym::homogeneous_aggregate, sym::debug], ); @@ -785,7 +795,8 @@ impl CombineAttributeParser for RustcMirParser { args: &ArgParser, ) -> impl IntoIterator { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return ThinVec::new(); }; @@ -800,34 +811,34 @@ impl CombineAttributeParser for RustcMirParser { sym::stop_after_dataflow => Some(RustcMirKind::StopAfterDataflow), sym::borrowck_graphviz_postflow => { let Some(nv) = mi.args().name_value() else { - cx.expected_name_value( + cx.adcx().expected_name_value( mi.span(), Some(sym::borrowck_graphviz_postflow), ); return None; }; let Some(path) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, None); + cx.adcx().expected_string_literal(nv.value_span, None); return None; }; let path = PathBuf::from(path.to_string()); if path.file_name().is_some() { Some(RustcMirKind::BorrowckGraphvizPostflow { path }) } else { - cx.expected_filename_literal(nv.value_span); + cx.adcx().expected_filename_literal(nv.value_span); None } } sym::borrowck_graphviz_format => { let Some(nv) = mi.args().name_value() else { - cx.expected_name_value( + cx.adcx().expected_name_value( mi.span(), Some(sym::borrowck_graphviz_format), ); return None; }; let Some(format) = nv.value_as_ident() else { - cx.expected_identifier(nv.value_span); + cx.adcx().expected_identifier(nv.value_span); return None; }; match format.name { @@ -835,7 +846,8 @@ impl CombineAttributeParser for RustcMirParser { format: BorrowckGraphvizFormatKind::TwoPhase, }), _ => { - cx.expected_specific_argument(format.span, &[sym::two_phase]); + cx.adcx() + .expected_specific_argument(format.span, &[sym::two_phase]); None } } @@ -906,7 +918,8 @@ impl CombineAttributeParser for RustcCleanParser { cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" }); } let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; let mut except = None; @@ -917,24 +930,24 @@ impl CombineAttributeParser for RustcCleanParser { let Some((value, name)) = item.meta_item().and_then(|m| Option::zip(m.args().name_value(), m.ident())) else { - cx.expected_name_value(item.span(), None); + cx.adcx().expected_name_value(item.span(), None); continue; }; let value_span = value.value_span; let Some(value) = value.value_as_str() else { - cx.expected_string_literal(value_span, None); + cx.adcx().expected_string_literal(value_span, None); continue; }; match name.name { sym::cfg if cfg.is_some() => { - cx.duplicate_key(item.span(), sym::cfg); + cx.adcx().duplicate_key(item.span(), sym::cfg); } sym::cfg => { cfg = Some(value); } sym::except if except.is_some() => { - cx.duplicate_key(item.span(), sym::except); + cx.adcx().duplicate_key(item.span(), sym::except); } sym::except => { let entries = @@ -942,7 +955,7 @@ impl CombineAttributeParser for RustcCleanParser { except = Some(RustcCleanQueries { entries, span: value_span }); } sym::loaded_from_disk if loaded_from_disk.is_some() => { - cx.duplicate_key(item.span(), sym::loaded_from_disk); + cx.adcx().duplicate_key(item.span(), sym::loaded_from_disk); } sym::loaded_from_disk => { let entries = @@ -950,7 +963,7 @@ impl CombineAttributeParser for RustcCleanParser { loaded_from_disk = Some(RustcCleanQueries { entries, span: value_span }); } _ => { - cx.expected_specific_argument( + cx.adcx().expected_specific_argument( name.span, &[sym::cfg, sym::except, sym::loaded_from_disk], ); @@ -958,7 +971,7 @@ impl CombineAttributeParser for RustcCleanParser { } } let Some(cfg) = cfg else { - cx.expected_specific_argument(list.span, &[sym::cfg]); + cx.adcx().expected_specific_argument(list.span, &[sym::cfg]); return None; }; @@ -1008,17 +1021,19 @@ impl SingleAttributeParser for RustcIfThisChangedParser { ArgParser::NoArgs => Some(AttributeKind::RustcIfThisChanged(cx.attr_span, None)), ArgParser::List(list) => { let Some(item) = list.single() else { - cx.expected_single_argument(list.span); + let attr_span = cx.attr_span; + cx.adcx().expected_single_argument(attr_span); return None; }; let Some(ident) = item.meta_item().and_then(|item| item.ident()) else { - cx.expected_identifier(item.span()); + cx.adcx().expected_identifier(item.span()); return None; }; Some(AttributeKind::RustcIfThisChanged(cx.attr_span, Some(ident.name))) } ArgParser::NameValue(_) => { - cx.expected_list_or_no_args(cx.inner_span); + let inner_span = cx.inner_span; + cx.adcx().expected_list_or_no_args(inner_span); None } } @@ -1068,11 +1083,12 @@ impl CombineAttributeParser for RustcThenThisWouldNeedParser { cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" }); } let Some(item) = args.list().and_then(|l| l.single()) else { - cx.expected_single_argument(cx.inner_span); + let inner_span = cx.inner_span; + cx.adcx().expected_single_argument(inner_span); return None; }; let Some(ident) = item.meta_item().and_then(|item| item.ident()) else { - cx.expected_identifier(item.span()); + cx.adcx().expected_identifier(item.span()); return None; }; Some(ident) @@ -1158,11 +1174,12 @@ impl SingleAttributeParser for RustcDiagnosticItemParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; let Some(value) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; Some(AttributeKind::RustcDiagnosticItem(value)) @@ -1210,7 +1227,7 @@ impl SingleAttributeParser for RustcSymbolNameParser { const TEMPLATE: AttributeTemplate = template!(Word); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { if let Err(span) = args.no_args() { - cx.expected_no_args(span); + cx.adcx().expected_no_args(span); return None; } Some(AttributeKind::RustcSymbolName(cx.attr_span)) @@ -1234,7 +1251,7 @@ impl SingleAttributeParser for RustcDefPathParser { const TEMPLATE: AttributeTemplate = template!(Word); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { if let Err(span) = args.no_args() { - cx.expected_no_args(span); + cx.adcx().expected_no_args(span); return None; } Some(AttributeKind::RustcDefPath(cx.attr_span)) @@ -1268,12 +1285,13 @@ impl SingleAttributeParser for RustcReservationImplParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { - cx.expected_name_value(args.span().unwrap_or(cx.attr_span), None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(args.span().unwrap_or(attr_span), None); return None; }; let Some(value_str) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; @@ -1300,12 +1318,13 @@ impl SingleAttributeParser for RustcDocPrimitiveParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { - cx.expected_name_value(args.span().unwrap_or(cx.attr_span), None); + let span = cx.attr_span; + cx.adcx().expected_name_value(args.span().unwrap_or(span), None); return None; }; let Some(value_str) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 25b295c162aab..6efcdd1e1b8da 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -102,11 +102,12 @@ impl AttributeParser for StabilityParser { |this, cx, args| { reject_outside_std!(cx); let Some(nv) = args.name_value() else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return; }; let Some(value_str) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return; }; this.allowed_through_unstable_modules = Some(value_str); @@ -281,13 +282,13 @@ impl AttributeParser for ConstStabilityParser { /// 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<'_, '_, S>, + cx: &mut AcceptContext<'_, '_, S>, param: &MetaItemParser, item: &mut Option, name: Ident, ) -> Option<()> { if item.is_some() { - cx.duplicate_key(name.span, name.name); + cx.adcx().duplicate_key(name.span, name.name); None } else if let Some(v) = param.args().name_value() && let Some(s) = v.value_as_str() @@ -295,7 +296,7 @@ fn insert_value_into_option_or_error( *item = Some(s); Some(()) } else { - cx.expected_name_value(param.span(), Some(name.name)); + cx.adcx().expected_name_value(param.span(), Some(name.name)); None } } @@ -303,21 +304,22 @@ 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<'_, '_, S>, + cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser, ) -> Option<(Symbol, StabilityLevel)> { let mut feature = None; let mut since = None; let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; for param in list.mixed() { let param_span = param.span(); let Some(param) = param.meta_item() else { - cx.unexpected_literal(param.span()); + cx.adcx().unexpected_literal(param.span()); return None; }; @@ -330,7 +332,7 @@ pub(crate) fn parse_stability( insert_value_into_option_or_error(cx, ¶m, &mut since, word.unwrap())? } _ => { - cx.expected_specific_argument(param_span, &[sym::feature, sym::since]); + cx.adcx().expected_specific_argument(param_span, &[sym::feature, sym::since]); return None; } } @@ -370,7 +372,7 @@ 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<'_, '_, S>, + cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser, ) -> Option<(Symbol, StabilityLevel)> { let mut feature = None; @@ -381,13 +383,14 @@ pub(crate) fn parse_unstability( let mut old_name = None; let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; for param in list.mixed() { let Some(param) = param.meta_item() else { - cx.unexpected_literal(param.span()); + cx.adcx().unexpected_literal(param.span()); return None; }; @@ -430,7 +433,7 @@ pub(crate) fn parse_unstability( insert_value_into_option_or_error(cx, ¶m, &mut old_name, word.unwrap())? } _ => { - cx.expected_specific_argument( + cx.adcx().expected_specific_argument( param.span(), &[sym::feature, sym::reason, sym::issue, sym::implied_by, sym::old_name], ); diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index db0350f23246e..e8d2bedc37802 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -22,13 +22,13 @@ impl SingleAttributeParser for IgnoreParser { ArgParser::NoArgs => None, ArgParser::NameValue(name_value) => { let Some(str_value) = name_value.value_as_str() else { - cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); + cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); return None; }; Some(str_value) } ArgParser::List(_) => { - cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); + cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); return None; } }, @@ -55,7 +55,7 @@ impl SingleAttributeParser for ShouldPanicParser { ArgParser::NoArgs => None, ArgParser::NameValue(name_value) => { let Some(str_value) = name_value.value_as_str() else { - cx.expected_string_literal( + cx.adcx().expected_string_literal( name_value.value_span, Some(name_value.value_as_lit()), ); @@ -65,23 +65,23 @@ impl SingleAttributeParser for ShouldPanicParser { } ArgParser::List(list) => { let Some(single) = list.single() else { - cx.expected_single_argument(list.span); + cx.adcx().expected_single_argument(list.span); return None; }; let Some(single) = single.meta_item() else { - cx.expected_name_value(single.span(), Some(sym::expected)); + cx.adcx().expected_name_value(single.span(), Some(sym::expected)); return None; }; if !single.path().word_is(sym::expected) { - cx.expected_specific_argument_strings(list.span, &[sym::expected]); + cx.adcx().expected_specific_argument_strings(list.span, &[sym::expected]); return None; } let Some(nv) = single.args().name_value() else { - cx.expected_name_value(single.span(), Some(sym::expected)); + cx.adcx().expected_name_value(single.span(), Some(sym::expected)); return None; }; let Some(expected) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; Some(expected) @@ -101,15 +101,16 @@ impl SingleAttributeParser for ReexportTestHarnessMainParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { - cx.expected_name_value( - args.span().unwrap_or(cx.inner_span), + let inner_span = cx.inner_span; + cx.adcx().expected_name_value( + args.span().unwrap_or(inner_span), Some(sym::reexport_test_harness_main), ); return None; }; let Some(name) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; @@ -135,17 +136,19 @@ impl SingleAttributeParser for RustcAbiParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(args) = args.list() else { - cx.expected_specific_argument_and_list(cx.attr_span, &[sym::assert_eq, sym::debug]); + let attr_span = cx.attr_span; + cx.adcx().expected_specific_argument_and_list(attr_span, &[sym::assert_eq, sym::debug]); return None; }; let Some(arg) = args.single() else { - cx.expected_single_argument(cx.attr_span); + let attr_span = cx.attr_span; + cx.adcx().expected_single_argument(attr_span); return None; }; - let fail_incorrect_argument = - |span| cx.expected_specific_argument(span, &[sym::assert_eq, sym::debug]); + let mut fail_incorrect_argument = + |span| cx.adcx().expected_specific_argument(span, &[sym::assert_eq, sym::debug]); let Some(arg) = arg.meta_item() else { fail_incorrect_argument(args.span); @@ -199,17 +202,18 @@ impl SingleAttributeParser for TestRunnerParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; let Some(single) = list.single() else { - cx.expected_single_argument(list.span); + cx.adcx().expected_single_argument(list.span); return None; }; let Some(meta) = single.meta_item() else { - cx.unexpected_literal(single.span()); + cx.adcx().unexpected_literal(single.span()); return None; }; @@ -231,17 +235,18 @@ impl SingleAttributeParser for RustcTestMarkerParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(name_value) = args.name_value() else { - cx.expected_name_value(cx.attr_span, Some(sym::rustc_test_marker)); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, Some(sym::rustc_test_marker)); return None; }; let Some(value_str) = name_value.value_as_str() else { - cx.expected_string_literal(name_value.value_span, None); + cx.adcx().expected_string_literal(name_value.value_span, None); return None; }; if value_str.as_str().trim().is_empty() { - cx.expected_non_empty_string_literal(name_value.value_span); + cx.adcx().expected_non_empty_string_literal(name_value.value_span); return None; } diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index c418d1d032c48..56447bf0f788a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -19,32 +19,34 @@ impl SingleAttributeParser for RustcSkipDuringMethodDispatchParser let mut array = false; let mut boxed_slice = false; let Some(args) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; if args.is_empty() { - cx.expected_at_least_one_argument(args.span); + cx.adcx().expected_at_least_one_argument(args.span); return None; } for arg in args.mixed() { let Some(arg) = arg.meta_item() else { - cx.unexpected_literal(arg.span()); + cx.adcx().unexpected_literal(arg.span()); continue; }; if let Err(span) = arg.args().no_args() { - cx.expected_no_args(span); + cx.adcx().expected_no_args(span); } let path = arg.path(); let (key, skip): (Symbol, &mut bool) = match path.word_sym() { Some(key @ sym::array) => (key, &mut array), Some(key @ sym::boxed_slice) => (key, &mut boxed_slice), _ => { - cx.expected_specific_argument(path.span(), &[sym::array, sym::boxed_slice]); + cx.adcx() + .expected_specific_argument(path.span(), &[sym::array, sym::boxed_slice]); continue; } }; if mem::replace(skip, true) { - cx.duplicate_key(arg.span(), key); + cx.adcx().duplicate_key(arg.span(), key); } } Some(AttributeKind::RustcSkipDuringMethodDispatch { diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index c3817406c9803..5685a2eccb35d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -15,7 +15,8 @@ impl SingleAttributeParser for RustcMacroTransparencyParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { - cx.expected_name_value(cx.attr_span, None); + let attr_span = cx.attr_span; + cx.adcx().expected_name_value(attr_span, None); return None; }; match nv.value_as_str() { @@ -23,7 +24,7 @@ impl SingleAttributeParser for RustcMacroTransparencyParser { Some(sym::semiopaque) => Some(Transparency::SemiOpaque), Some(sym::opaque) => Some(Transparency::Opaque), Some(_) => { - cx.expected_specific_argument_strings( + cx.adcx().expected_specific_argument_strings( nv.value_span, &[sym::transparent, sym::semiopaque, sym::opaque], ); diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index ebec8e8443fc8..4bd9b2b963e9a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -42,28 +42,29 @@ pub(crate) fn parse_single_integer( args: &ArgParser, ) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + let attr_span = cx.attr_span; + cx.adcx().expected_list(attr_span, args); return None; }; let Some(single) = list.single() else { - cx.expected_single_argument(list.span); + cx.adcx().expected_single_argument(list.span); return None; }; let Some(lit) = single.lit() else { - cx.expected_integer_literal(single.span()); + cx.adcx().expected_integer_literal(single.span()); return None; }; let LitKind::Int(num, _ty) = lit.kind else { - cx.expected_integer_literal(single.span()); + cx.adcx().expected_integer_literal(single.span()); return None; }; Some(num.0) } impl AcceptContext<'_, '_, S> { - pub(crate) fn parse_limit_int(&self, nv: &NameValueParser) -> Option { + pub(crate) fn parse_limit_int(&mut self, nv: &NameValueParser) -> Option { let Some(limit) = nv.value_as_str() else { - self.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + self.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 259a73de59853..2bab0019eaf96 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -1,6 +1,7 @@ use std::cell::RefCell; use std::collections::BTreeMap; use std::collections::btree_map::Entry; +use std::mem; use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; @@ -60,7 +61,8 @@ use crate::attributes::transparency::*; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; use crate::parser::{ArgParser, RefPathParser}; use crate::session_diagnostics::{ - AttributeParseError, AttributeParseErrorReason, ParsedDescription, + AttributeParseError, AttributeParseErrorReason, AttributeParseErrorSuggestions, + ParsedDescription, }; use crate::target_checking::AllowedTargets; type GroupType = LazyLock>; @@ -487,11 +489,147 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { } impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { + pub(crate) fn adcx(&mut self) -> AttributeDiagnosticContext<'_, 'f, 'sess, S> { + AttributeDiagnosticContext { ctx: self, custom_suggestions: Vec::new() } + } +} + +impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> { + type Target = SharedContext<'f, 'sess, S>; + + fn deref(&self) -> &Self::Target { + &self.shared + } +} + +impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.shared + } +} + +/// Context given to every attribute parser during finalization. +/// +/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create +/// errors, for example. +pub struct SharedContext<'p, 'sess, S: Stage> { + /// The parse context, gives access to the session and the + /// diagnostics context. + pub(crate) cx: &'p mut AttributeParser<'sess, S>, + /// The span of the syntactical component this attribute was applied to + pub(crate) target_span: Span, + pub(crate) target: rustc_hir::Target, + + /// The second argument of the closure is a [`NodeId`] if `S` is `Early` and a [`HirId`] if `S` + /// is `Late` and is the ID of the syntactical component this attribute was applied to. + pub(crate) emit_lint: &'p mut dyn FnMut(LintId, Span, AttributeLintKind), +} + +/// 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<'p, 'sess, S: Stage> { + pub(crate) shared: SharedContext<'p, 'sess, S>, + + /// A list of all attribute on this syntax node. + /// + /// Useful for compatibility checks with other attributes in [`finalize`](crate::attributes::AttributeParser::finalize) + /// + /// Usually, you should use normal attribute parsing logic instead, + /// especially when making a *denylist* of other attributes. + pub(crate) all_attrs: &'p [RefPathParser<'p>], +} + +impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> { + type Target = SharedContext<'p, 'sess, S>; + + fn deref(&self) -> &Self::Target { + &self.shared + } +} + +impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.shared + } +} + +impl<'p, 'sess: 'p, S: Stage> Deref for SharedContext<'p, 'sess, S> { + type Target = AttributeParser<'sess, S>; + + fn deref(&self) -> &Self::Target { + self.cx + } +} + +impl<'p, 'sess: 'p, S: Stage> DerefMut for SharedContext<'p, 'sess, S> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.cx + } +} + +#[derive(PartialEq, Clone, Copy, Debug)] +pub enum OmitDoc { + Lower, + Skip, +} + +#[derive(Copy, Clone, Debug)] +pub enum ShouldEmit { + /// The operations will emit errors, and lints, and errors are fatal. + /// + /// Only relevant when early parsing, in late parsing equivalent to `ErrorsAndLints`. + /// Late parsing is never fatal, and instead tries to emit as many diagnostics as possible. + EarlyFatal { also_emit_lints: bool }, + /// The operation will emit errors and lints. + /// This is usually what you need. + ErrorsAndLints { + /// Whether [`ArgParser`] will attempt to recover from errors. + /// + /// Whether it is allowed to recover from bad input (like an invalid literal). Setting + /// this to `Forbidden` will instead return early, and not raise errors except at the top + /// level (in [`ArgParser::from_attr_args`]). + recovery: Recovery, + }, + /// The operation will *not* emit errors and lints. + /// + /// The parser can still call `delay_bug`, so you *must* ensure that this operation will also be + /// called with `ShouldEmit::ErrorsAndLints`. + Nothing, +} + +impl ShouldEmit { + pub(crate) fn emit_err(&self, diag: Diag<'_>) -> ErrorGuaranteed { + match self { + ShouldEmit::EarlyFatal { .. } if diag.level() == Level::DelayedBug => diag.emit(), + ShouldEmit::EarlyFatal { .. } => diag.upgrade_to_fatal().emit(), + ShouldEmit::ErrorsAndLints { .. } => diag.emit(), + ShouldEmit::Nothing => diag.delay_as_bug(), + } + } +} + +pub(crate) struct AttributeDiagnosticContext<'a, 'f, 'sess, S: Stage> { + ctx: &'a mut AcceptContext<'f, 'sess, S>, + custom_suggestions: Vec, +} + +impl<'a, 'f, 'sess: 'f, S> AttributeDiagnosticContext<'a, 'f, 'sess, S> +where + S: Stage, +{ fn emit_parse_error( - &self, + &mut self, span: Span, reason: AttributeParseErrorReason<'_>, ) -> ErrorGuaranteed { + let suggestions = if !self.custom_suggestions.is_empty() { + AttributeParseErrorSuggestions::CreatedByParser(mem::take(&mut self.custom_suggestions)) + } else { + AttributeParseErrorSuggestions::CreatedByTemplate(self.template_suggestions()) + }; + self.emit_err(AttributeParseError { span, attr_span: self.attr_span, @@ -499,40 +637,38 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { path: self.attr_path.clone(), description: self.parsed_description, reason, - suggestions: self.suggestions(), + suggestions, }) } - /// error that a string literal was expected. - /// You can optionally give the literal you did find (which you found not to be a string literal) - /// which can make better errors. For example, if the literal was a byte string it will suggest - /// removing the `b` prefix. - pub(crate) fn expected_string_literal( - &self, - span: Span, - actual_literal: Option<&MetaItemLit>, - ) -> ErrorGuaranteed { - self.emit_parse_error( - span, - AttributeParseErrorReason::ExpectedStringLiteral { - byte_string: actual_literal.and_then(|i| { - i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span)) - }), - }, - ) + /// Adds a custom suggestion to the diagnostic. This also prevents the default (template-based) + /// suggestion to be emitted. + pub(crate) fn push_suggestion(&mut self, msg: String, span: Span, code: String) -> &mut Self { + self.custom_suggestions.push(Suggestion { msg, sp: span, code }); + self } - /// Error that a filename string literal was expected. - pub(crate) fn expected_filename_literal(&self, span: Span) { - self.emit_parse_error(span, AttributeParseErrorReason::ExpectedFilenameLiteral); - } + pub(crate) fn template_suggestions(&self) -> Vec { + let style = match self.parsed_description { + // If the outer and inner spans are equal, we are parsing an embedded attribute + ParsedDescription::Attribute if self.attr_span == self.inner_span => { + AttrSuggestionStyle::EmbeddedAttribute + } + ParsedDescription::Attribute => AttrSuggestionStyle::Attribute(self.attr_style), + ParsedDescription::Macro => AttrSuggestionStyle::Macro, + }; - pub(crate) fn expected_integer_literal(&self, span: Span) -> ErrorGuaranteed { - self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral) + self.template.suggestions(style, &self.attr_path) } +} +/// Helpers that can be used to generate errors during attribute parsing. +impl<'a, 'f, 'sess: 'f, S> AttributeDiagnosticContext<'a, 'f, 'sess, S> +where + S: Stage, +{ pub(crate) fn expected_integer_literal_in_range( - &self, + &mut self, span: Span, lower_bound: isize, upper_bound: isize, @@ -543,7 +679,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { ) } - pub(crate) fn expected_list(&self, span: Span, args: &ArgParser) -> ErrorGuaranteed { + pub(crate) fn expected_list(&mut self, span: Span, args: &ArgParser) -> ErrorGuaranteed { let span = match args { ArgParser::NoArgs => span, ArgParser::List(list) => list.span, @@ -553,7 +689,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { } pub(crate) fn expected_list_with_num_args_or_more( - &self, + &mut self, args: usize, span: Span, ) -> ErrorGuaranteed { @@ -563,55 +699,59 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { ) } - pub(crate) fn expected_list_or_no_args(&self, span: Span) -> ErrorGuaranteed { + pub(crate) fn expected_list_or_no_args(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedListOrNoArgs) } - pub(crate) fn expected_nv_or_no_args(&self, span: Span) -> ErrorGuaranteed { + pub(crate) fn expected_nv_or_no_args(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValueOrNoArgs) } - pub(crate) fn expected_non_empty_string_literal(&self, span: Span) -> ErrorGuaranteed { + pub(crate) fn expected_non_empty_string_literal(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNonEmptyStringLiteral) } - pub(crate) fn expected_no_args(&self, span: Span) -> ErrorGuaranteed { + pub(crate) fn expected_no_args(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNoArgs) } /// emit an error that a `name` was expected here - pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed { + pub(crate) fn expected_identifier(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIdentifier) } /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for /// a nicer error message talking about the specific name that was found lacking a value. - pub(crate) fn expected_name_value(&self, span: Span, name: Option) -> ErrorGuaranteed { + pub(crate) fn expected_name_value( + &mut self, + span: Span, + name: Option, + ) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValue(name)) } /// emit an error that a `name = value` pair was found where that name was already seen. - pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed { + pub(crate) fn duplicate_key(&mut self, span: Span, key: Symbol) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::DuplicateKey(key)) } /// an error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser) /// was expected *not* to be a literal, but instead a meta item. - pub(crate) fn unexpected_literal(&self, span: Span) -> ErrorGuaranteed { + pub(crate) fn unexpected_literal(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::UnexpectedLiteral) } - pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed { + pub(crate) fn expected_single_argument(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedSingleArgument) } - pub(crate) fn expected_at_least_one_argument(&self, span: Span) -> ErrorGuaranteed { + pub(crate) fn expected_at_least_one_argument(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedAtLeastOneArgument) } /// produces an error along the lines of `expected one of [foo, meow]` pub(crate) fn expected_specific_argument( - &self, + &mut self, span: Span, possibilities: &[Symbol], ) -> ErrorGuaranteed { @@ -628,7 +768,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { /// produces an error along the lines of `expected one of [foo, meow] as an argument`. /// i.e. slightly different wording to [`expected_specific_argument`](Self::expected_specific_argument). pub(crate) fn expected_specific_argument_and_list( - &self, + &mut self, span: Span, possibilities: &[Symbol], ) -> ErrorGuaranteed { @@ -644,7 +784,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { /// produces an error along the lines of `expected one of ["foo", "meow"]` pub(crate) fn expected_specific_argument_strings( - &self, + &mut self, span: Span, possibilities: &[Symbol], ) -> ErrorGuaranteed { @@ -690,120 +830,58 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { self.template.suggestions(style, &self.attr_path) } -} - -impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> { - type Target = SharedContext<'f, 'sess, S>; - - fn deref(&self) -> &Self::Target { - &self.shared - } -} - -impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.shared + /// error that a string literal was expected. + /// You can optionally give the literal you did find (which you found not to be a string literal) + /// which can make better errors. For example, if the literal was a byte string it will suggest + /// removing the `b` prefix. + pub(crate) fn expected_string_literal( + &mut self, + span: Span, + actual_literal: Option<&MetaItemLit>, + ) -> ErrorGuaranteed { + self.emit_parse_error( + span, + AttributeParseErrorReason::ExpectedStringLiteral { + byte_string: actual_literal.and_then(|i| { + i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span)) + }), + }, + ) } -} - -/// Context given to every attribute parser during finalization. -/// -/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create -/// errors, for example. -pub struct SharedContext<'p, 'sess, S: Stage> { - /// The parse context, gives access to the session and the - /// diagnostics context. - pub(crate) cx: &'p mut AttributeParser<'sess, S>, - /// The span of the syntactical component this attribute was applied to - pub(crate) target_span: Span, - pub(crate) target: rustc_hir::Target, - - /// The second argument of the closure is a [`NodeId`] if `S` is `Early` and a [`HirId`] if `S` - /// is `Late` and is the ID of the syntactical component this attribute was applied to. - pub(crate) emit_lint: &'p mut dyn FnMut(LintId, Span, AttributeLintKind), -} -/// 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<'p, 'sess, S: Stage> { - pub(crate) shared: SharedContext<'p, 'sess, S>, - - /// A list of all attribute on this syntax node. - /// - /// Useful for compatibility checks with other attributes in [`finalize`](crate::attributes::AttributeParser::finalize) - /// - /// Usually, you should use normal attribute parsing logic instead, - /// especially when making a *denylist* of other attributes. - pub(crate) all_attrs: &'p [RefPathParser<'p>], -} - -impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> { - type Target = SharedContext<'p, 'sess, S>; - - fn deref(&self) -> &Self::Target { - &self.shared + /// Error that a filename string literal was expected. + pub(crate) fn expected_filename_literal(&mut self, span: Span) { + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedFilenameLiteral); } -} -impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.shared + pub(crate) fn expected_integer_literal(&mut self, span: Span) -> ErrorGuaranteed { + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral) } } -impl<'p, 'sess: 'p, S: Stage> Deref for SharedContext<'p, 'sess, S> { - type Target = AttributeParser<'sess, S>; +impl<'a, 'f, 'sess: 'f, S> Deref for AttributeDiagnosticContext<'a, 'f, 'sess, S> +where + S: Stage, +{ + type Target = AcceptContext<'f, 'sess, S>; fn deref(&self) -> &Self::Target { - self.cx + self.ctx } } -impl<'p, 'sess: 'p, S: Stage> DerefMut for SharedContext<'p, 'sess, S> { +impl<'a, 'f, 'sess: 'f, S> DerefMut for AttributeDiagnosticContext<'a, 'f, 'sess, S> +where + S: Stage, +{ fn deref_mut(&mut self) -> &mut Self::Target { - self.cx + self.ctx } } -#[derive(PartialEq, Clone, Copy, Debug)] -pub enum OmitDoc { - Lower, - Skip, -} - -#[derive(Copy, Clone, Debug)] -pub enum ShouldEmit { - /// The operations will emit errors, and lints, and errors are fatal. - /// - /// Only relevant when early parsing, in late parsing equivalent to `ErrorsAndLints`. - /// Late parsing is never fatal, and instead tries to emit as many diagnostics as possible. - EarlyFatal { also_emit_lints: bool }, - /// The operation will emit errors and lints. - /// This is usually what you need. - ErrorsAndLints { - /// Whether [`ArgParser`] will attempt to recover from errors. - /// - /// Whether it is allowed to recover from bad input (like an invalid literal). Setting - /// this to `Forbidden` will instead return early, and not raise errors except at the top - /// level (in [`ArgParser::from_attr_args`]). - recovery: Recovery, - }, - /// The operation will *not* emit errors and lints. - /// - /// The parser can still call `delay_bug`, so you *must* ensure that this operation will also be - /// called with `ShouldEmit::ErrorsAndLints`. - Nothing, -} - -impl ShouldEmit { - pub(crate) fn emit_err(&self, diag: Diag<'_>) -> ErrorGuaranteed { - match self { - ShouldEmit::EarlyFatal { .. } if diag.level() == Level::DelayedBug => diag.emit(), - ShouldEmit::EarlyFatal { .. } => diag.upgrade_to_fatal().emit(), - ShouldEmit::ErrorsAndLints { .. } => diag.emit(), - ShouldEmit::Nothing => diag.delay_as_bug(), - } - } +/// Represents a custom suggestion that an attribute parser can emit. +pub(crate) struct Suggestion { + pub(crate) msg: String, + pub(crate) sp: Span, + pub(crate) code: String, } diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 6e8a500967363..a7daec6d6096c 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -201,7 +201,7 @@ impl ArgParser { /// Assert that there were no args. /// If there were, get a span to the arguments - /// (to pass to [`AcceptContext::expected_no_args`](crate::context::AcceptContext::expected_no_args)). + /// (to pass to [`AttributeDiagnosticContext::expected_no_args`](crate::context::AttributeDiagnosticContext::expected_no_args)). pub fn no_args(&self) -> Result<(), Span> { match self { Self::NoArgs => Ok(()), diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 7321260972157..554a3192968e7 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -12,6 +12,8 @@ use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; use rustc_target::spec::TargetTuple; +use crate::context::Suggestion; + #[derive(Diagnostic)] #[diag("invalid predicate `{$predicate}`", code = E0537)] pub(crate) struct InvalidPredicate { @@ -591,7 +593,12 @@ pub(crate) struct AttributeParseError<'a> { pub(crate) path: AttrPath, pub(crate) description: ParsedDescription, pub(crate) reason: AttributeParseErrorReason<'a>, - pub(crate) suggestions: Vec, + pub(crate) suggestions: AttributeParseErrorSuggestions, +} + +pub(crate) enum AttributeParseErrorSuggestions { + CreatedByTemplate(Vec), + CreatedByParser(Vec), } impl<'a> AttributeParseError<'a> { @@ -666,6 +673,41 @@ impl<'a> AttributeParseError<'a> { } } + fn render_suggestions(&self, diag: &mut Diag<'_, G>) + where + G: EmissionGuarantee, + { + let description = self.description(); + + match &self.suggestions { + AttributeParseErrorSuggestions::CreatedByTemplate(suggestions) => { + diag.span_suggestions( + self.attr_span, + if suggestions.len() == 1 { + "must be of the form".to_string() + } else { + format!( + "try changing it to one of the following valid forms of the {description}" + ) + }, + suggestions.iter().cloned(), + Applicability::HasPlaceholders, + ); + } + + AttributeParseErrorSuggestions::CreatedByParser(suggestions) => { + for Suggestion { msg, sp, code } in suggestions { + diag.span_suggestion_verbose( + *sp, + msg.to_string(), + code.to_string(), + Applicability::MaybeIncorrect, + ); + } + } + } + } + fn description(&self) -> &'static str { match self.description { ParsedDescription::Attribute => "attribute", @@ -674,6 +716,15 @@ impl<'a> AttributeParseError<'a> { } } +impl AttributeParseErrorSuggestions { + fn len(&self) -> usize { + match self { + AttributeParseErrorSuggestions::CreatedByTemplate(items) => items.len(), + AttributeParseErrorSuggestions::CreatedByParser(items) => items.len(), + } + } +} + impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let name = self.path.to_string(); @@ -791,18 +842,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { } if self.suggestions.len() < 4 { - diag.span_suggestions( - self.attr_span, - if self.suggestions.len() == 1 { - "must be of the form".to_string() - } else { - format!( - "try changing it to one of the following valid forms of the {description}" - ) - }, - self.suggestions, - Applicability::HasPlaceholders, - ); + self.render_suggestions(&mut diag); } diag From 4e019ac160fa96628016bc308a2bccd8962a3513 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Sun, 29 Mar 2026 10:32:11 +0000 Subject: [PATCH 08/11] make `cfg` parser suggest `any` or `all` on `#[cfg(a, b)]` --- .../rustc_attr_parsing/src/attributes/cfg.rs | 31 ++++++++++++- tests/rustdoc-ui/doc-cfg.stderr | 32 ++++++++++++++ tests/rustdoc-ui/invalid-cfg.stderr | 44 +++++++++++++++++++ tests/ui/cfg/suggest-any-or-all.stderr | 24 +++++++--- .../cfg-attr-syntax-validation.stderr | 24 +++++++--- 5 files changed, 142 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index c3b693425a52b..ccc4a1a64c56f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -48,8 +48,37 @@ pub fn parse_cfg( cx.adcx().expected_list(attr_span, args); return None; }; + let Some(single) = list.single() else { - cx.adcx().expected_single_argument(list.span); + let target = cx.target; + let mut adcx = cx.adcx(); + if list.is_empty() { + // `#[cfg()]` + let message = format!("if the {target} should be disabled, use `#[cfg(false)]`"); + adcx.push_suggestion(message, list.span, "(false)".to_string()); + } else { + // `#[cfg(foo, bar)]` + if let Ok(args) = adcx + .sess() + .source_map() + .span_to_source(list.span, |src, start, end| Ok(src[start..end].to_string())) + { + let all = format!("(all{args})"); + let any = format!("(any{args})"); + + let all_msg = format!( + "if the {target} should be enabled when all these predicates are, wrap them in `all`" + ); + let any_msg = format!( + "alternately, if the {target} should be enabled when any of these predicates are, wrap them in `any`" + ); + + adcx.push_suggestion(all_msg, list.span, all); + adcx.push_suggestion(any_msg, list.span, any); + } + } + + adcx.expected_single_argument(list.span); return None; }; parse_cfg_entry(cx, single).ok() diff --git a/tests/rustdoc-ui/doc-cfg.stderr b/tests/rustdoc-ui/doc-cfg.stderr index fa25a441e9b79..db525ca7a807b 100644 --- a/tests/rustdoc-ui/doc-cfg.stderr +++ b/tests/rustdoc-ui/doc-cfg.stderr @@ -5,6 +5,11 @@ LL | #[doc(cfg(), cfg(foo, bar))] | ^^^^^^^^^--^^^^^^^^^^^^^^^^^ | | | expected a single argument here + | +help: if the function should be disabled, use `#[cfg(false)]` + | +LL | #[doc(cfg(false), cfg(foo, bar))] + | +++++ error[E0805]: malformed `doc` attribute input --> $DIR/doc-cfg.rs:4:1 @@ -13,6 +18,17 @@ LL | #[doc(cfg(), cfg(foo, bar))] | ^^^^^^^^^^^^^^^^----------^^ | | | expected a single argument here + | +help: if the function should be enabled when all these predicates are, wrap them in `all` + | +LL - #[doc(cfg(), cfg(foo, bar))] +LL + #[doc(cfg(), cfg(all(foo, bar)))] + | +help: alternately, if the function should be enabled when any of these predicates are, wrap them in `any` + | +LL - #[doc(cfg(), cfg(foo, bar))] +LL + #[doc(cfg(), cfg(any(foo, bar)))] + | error[E0805]: malformed `doc` attribute input --> $DIR/doc-cfg.rs:7:1 @@ -21,6 +37,11 @@ LL | #[doc(cfg())] | ^^^^^^^^^--^^ | | | expected a single argument here + | +help: if the function should be disabled, use `#[cfg(false)]` + | +LL | #[doc(cfg(false))] + | +++++ error[E0805]: malformed `doc` attribute input --> $DIR/doc-cfg.rs:8:1 @@ -29,6 +50,17 @@ LL | #[doc(cfg(foo, bar))] | ^^^^^^^^^----------^^ | | | expected a single argument here + | +help: if the function should be enabled when all these predicates are, wrap them in `all` + | +LL - #[doc(cfg(foo, bar))] +LL + #[doc(cfg(all(foo, bar)))] + | +help: alternately, if the function should be enabled when any of these predicates are, wrap them in `any` + | +LL - #[doc(cfg(foo, bar))] +LL + #[doc(cfg(any(foo, bar)))] + | error: aborting due to 4 previous errors diff --git a/tests/rustdoc-ui/invalid-cfg.stderr b/tests/rustdoc-ui/invalid-cfg.stderr index 5396110709692..60b4033713622 100644 --- a/tests/rustdoc-ui/invalid-cfg.stderr +++ b/tests/rustdoc-ui/invalid-cfg.stderr @@ -13,6 +13,17 @@ LL | #[doc(cfg(x, y))] | ^^^^^^^^^------^^ | | | expected a single argument here + | +help: if the struct should be enabled when all these predicates are, wrap them in `all` + | +LL - #[doc(cfg(x, y))] +LL + #[doc(cfg(all(x, y)))] + | +help: alternately, if the struct should be enabled when any of these predicates are, wrap them in `any` + | +LL - #[doc(cfg(x, y))] +LL + #[doc(cfg(any(x, y)))] + | error[E0539]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:7:1 @@ -29,6 +40,17 @@ LL | #[doc(cfg(x, y))] | ^^^^^^^^^------^^ | | | expected a single argument here + | +help: if the struct should be enabled when all these predicates are, wrap them in `all` + | +LL - #[doc(cfg(x, y))] +LL + #[doc(cfg(all(x, y)))] + | +help: alternately, if the struct should be enabled when any of these predicates are, wrap them in `any` + | +LL - #[doc(cfg(x, y))] +LL + #[doc(cfg(any(x, y)))] + | error[E0539]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:12:1 @@ -45,6 +67,17 @@ LL | #[doc(cfg(x, y))] | ^^^^^^^^^------^^ | | | expected a single argument here + | +help: if the struct should be enabled when all these predicates are, wrap them in `all` + | +LL - #[doc(cfg(x, y))] +LL + #[doc(cfg(all(x, y)))] + | +help: alternately, if the struct should be enabled when any of these predicates are, wrap them in `any` + | +LL - #[doc(cfg(x, y))] +LL + #[doc(cfg(any(x, y)))] + | error[E0539]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:18:1 @@ -61,6 +94,17 @@ LL | #[doc(cfg(x, y))] | ^^^^^^^^^------^^ | | | expected a single argument here + | +help: if the struct should be enabled when all these predicates are, wrap them in `all` + | +LL - #[doc(cfg(x, y))] +LL + #[doc(cfg(all(x, y)))] + | +help: alternately, if the struct should be enabled when any of these predicates are, wrap them in `any` + | +LL - #[doc(cfg(x, y))] +LL + #[doc(cfg(any(x, y)))] + | error: aborting due to 8 previous errors diff --git a/tests/ui/cfg/suggest-any-or-all.stderr b/tests/ui/cfg/suggest-any-or-all.stderr index 596a49f261036..67ff6f178d128 100644 --- a/tests/ui/cfg/suggest-any-or-all.stderr +++ b/tests/ui/cfg/suggest-any-or-all.stderr @@ -3,22 +3,34 @@ error[E0805]: malformed `cfg` attribute input | LL | #[cfg(foo, bar)] | ^^^^^----------^ - | | | - | | expected a single argument here - | help: must be of the form: `#[cfg(predicate)]` + | | + | expected a single argument here | = note: for more information, visit +help: if the crate should be enabled when all these predicates are, wrap them in `all` + | +LL - #[cfg(foo, bar)] +LL + #[cfg(all(foo, bar))] + | +help: alternately, if the crate should be enabled when any of these predicates are, wrap them in `any` + | +LL - #[cfg(foo, bar)] +LL + #[cfg(any(foo, bar))] + | error[E0805]: malformed `cfg` attribute input --> $DIR/suggest-any-or-all.rs:5:1 | LL | #[cfg()] | ^^^^^--^ - | | | - | | expected a single argument here - | help: must be of the form: `#[cfg(predicate)]` + | | + | expected a single argument here | = note: for more information, visit +help: if the crate should be disabled, use `#[cfg(false)]` + | +LL | #[cfg(false)] + | +++++ error: aborting due to 2 previous errors diff --git a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr index 1be52de708e5b..ddb5cd84097f1 100644 --- a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr +++ b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr @@ -25,22 +25,34 @@ error[E0805]: malformed `cfg` attribute input | LL | #[cfg()] | ^^^^^--^ - | | | - | | expected a single argument here - | help: must be of the form: `#[cfg(predicate)]` + | | + | expected a single argument here | = note: for more information, visit +help: if the crate should be disabled, use `#[cfg(false)]` + | +LL | #[cfg(false)] + | +++++ error[E0805]: malformed `cfg` attribute input --> $DIR/cfg-attr-syntax-validation.rs:19:1 | LL | #[cfg(a, b)] | ^^^^^------^ - | | | - | | expected a single argument here - | help: must be of the form: `#[cfg(predicate)]` + | | + | expected a single argument here | = note: for more information, visit +help: if the crate should be enabled when all these predicates are, wrap them in `all` + | +LL - #[cfg(a, b)] +LL + #[cfg(all(a, b))] + | +help: alternately, if the crate should be enabled when any of these predicates are, wrap them in `any` + | +LL - #[cfg(a, b)] +LL + #[cfg(any(a, b))] + | error[E0539]: malformed `cfg` attribute input --> $DIR/cfg-attr-syntax-validation.rs:25:1 From eb20cada00d0623dc0fd367e24f29384d29c472a Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Mon, 30 Mar 2026 14:07:12 +0000 Subject: [PATCH 09/11] make sure the right target is passed to `#[cfg()]` when it is parsed --- compiler/rustc_expand/src/expand.rs | 90 ++++++++++++++++++- tests/ui/cfg/suggest-any-or-all.stderr | 6 +- .../cfg-attr-syntax-validation.stderr | 6 +- 3 files changed, 93 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index c8ef295b2a79d..98e097ac40026 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1306,6 +1306,8 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { fn declared_idents(&self) -> Vec { vec![] } + + fn as_target(&self) -> Target; } impl InvocationCollectorNode for Box { @@ -1457,6 +1459,10 @@ impl InvocationCollectorNode for Box { self.kind.ident().into_iter().collect() } } + + fn as_target(&self) -> Target { + Target::from_ast_item(&*self) + } } struct TraitItemTag; @@ -1498,6 +1504,9 @@ impl InvocationCollectorNode for AstNodeWrapper, TraitItemTa fn flatten_outputs(items: impl Iterator) -> Self::OutputTy { items.flatten().collect() } + fn as_target(&self) -> Target { + Target::from_assoc_item_kind(&self.wrapped.kind, AssocCtxt::Trait) + } } struct ImplItemTag; @@ -1539,6 +1548,9 @@ impl InvocationCollectorNode for AstNodeWrapper, ImplItemTag fn flatten_outputs(items: impl Iterator) -> Self::OutputTy { items.flatten().collect() } + fn as_target(&self) -> Target { + Target::from_assoc_item_kind(&self.wrapped.kind, AssocCtxt::Impl { of_trait: false }) + } } struct TraitImplItemTag; @@ -1580,6 +1592,9 @@ impl InvocationCollectorNode for AstNodeWrapper, TraitImplIt fn flatten_outputs(items: impl Iterator) -> Self::OutputTy { items.flatten().collect() } + fn as_target(&self) -> Target { + Target::from_assoc_item_kind(&self.wrapped.kind, AssocCtxt::Impl { of_trait: true }) + } } impl InvocationCollectorNode for Box { @@ -1602,6 +1617,14 @@ impl InvocationCollectorNode for Box { _ => unreachable!(), } } + fn as_target(&self) -> Target { + match &self.kind { + ForeignItemKind::Static(_) => Target::ForeignStatic, + ForeignItemKind::Fn(_) => Target::ForeignFn, + ForeignItemKind::TyAlias(_) => Target::ForeignTy, + ForeignItemKind::MacCall(_) => Target::MacroCall, + } + } } impl InvocationCollectorNode for ast::Variant { @@ -1615,6 +1638,9 @@ impl InvocationCollectorNode for ast::Variant { fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { walk_flat_map_variant(collector, self) } + fn as_target(&self) -> Target { + Target::Variant + } } impl InvocationCollectorNode for ast::WherePredicate { @@ -1628,6 +1654,9 @@ impl InvocationCollectorNode for ast::WherePredicate { fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { walk_flat_map_where_predicate(collector, self) } + fn as_target(&self) -> Target { + Target::WherePredicate + } } impl InvocationCollectorNode for ast::FieldDef { @@ -1641,6 +1670,9 @@ impl InvocationCollectorNode for ast::FieldDef { fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { walk_flat_map_field_def(collector, self) } + fn as_target(&self) -> Target { + Target::Field + } } impl InvocationCollectorNode for ast::PatField { @@ -1654,6 +1686,9 @@ impl InvocationCollectorNode for ast::PatField { fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { walk_flat_map_pat_field(collector, self) } + fn as_target(&self) -> Target { + Target::PatField + } } impl InvocationCollectorNode for ast::ExprField { @@ -1667,6 +1702,9 @@ impl InvocationCollectorNode for ast::ExprField { fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { walk_flat_map_expr_field(collector, self) } + fn as_target(&self) -> Target { + Target::ExprField + } } impl InvocationCollectorNode for ast::Param { @@ -1680,6 +1718,9 @@ impl InvocationCollectorNode for ast::Param { fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { walk_flat_map_param(collector, self) } + fn as_target(&self) -> Target { + Target::Param + } } impl InvocationCollectorNode for ast::GenericParam { @@ -1693,6 +1734,25 @@ impl InvocationCollectorNode for ast::GenericParam { fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { walk_flat_map_generic_param(collector, self) } + fn as_target(&self) -> Target { + let mut has_default = false; + Target::GenericParam { + kind: match &self.kind { + rustc_ast::GenericParamKind::Lifetime => { + rustc_hir::target::GenericParamKind::Lifetime + } + rustc_ast::GenericParamKind::Type { default } => { + has_default = default.is_some(); + rustc_hir::target::GenericParamKind::Type + } + rustc_ast::GenericParamKind::Const { default, .. } => { + has_default = default.is_some(); + rustc_hir::target::GenericParamKind::Const + } + }, + has_default, + } + } } impl InvocationCollectorNode for ast::Arm { @@ -1706,6 +1766,9 @@ impl InvocationCollectorNode for ast::Arm { fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { walk_flat_map_arm(collector, self) } + fn as_target(&self) -> Target { + Target::Arm + } } impl InvocationCollectorNode for ast::Stmt { @@ -1779,6 +1842,9 @@ impl InvocationCollectorNode for ast::Stmt { } } } + fn as_target(&self) -> Target { + Target::Statement + } } impl InvocationCollectorNode for ast::Crate { @@ -1805,6 +1871,9 @@ impl InvocationCollectorNode for ast::Crate { // Standard prelude imports are left in the crate for backward compatibility. self.items.truncate(collector.cx.num_standard_library_imports); } + fn as_target(&self) -> Target { + Target::Crate + } } impl InvocationCollectorNode for ast::Ty { @@ -1838,6 +1907,10 @@ impl InvocationCollectorNode for ast::Ty { _ => unreachable!(), } } + fn as_target(&self) -> Target { + // This is only used for attribute parsing, which are not allowed on types. + unreachable!() + } } impl InvocationCollectorNode for ast::Pat { @@ -1861,6 +1934,9 @@ impl InvocationCollectorNode for ast::Pat { _ => unreachable!(), } } + fn as_target(&self) -> Target { + todo!(); + } } impl InvocationCollectorNode for ast::Expr { @@ -1887,6 +1963,9 @@ impl InvocationCollectorNode for ast::Expr { _ => unreachable!(), } } + fn as_target(&self) -> Target { + Target::Expression + } } struct OptExprTag; @@ -1916,6 +1995,9 @@ impl InvocationCollectorNode for AstNodeWrapper, OptExprTag> { fn pre_flat_map_node_collect_attr(cfg: &StripUnconfigured<'_>, attr: &ast::Attribute) { cfg.maybe_emit_expr_attr_err(attr); } + fn as_target(&self) -> Target { + Target::Expression + } } /// This struct is a hack to workaround unstable of `stmt_expr_attributes`. @@ -1947,6 +2029,9 @@ impl InvocationCollectorNode for AstNodeWrapper { _ => unreachable!(), } } + fn as_target(&self) -> Target { + Target::Expression + } } fn build_single_delegations<'a, Node: InvocationCollectorNode>( @@ -2210,7 +2295,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn expand_cfg_true( &mut self, - node: &mut (impl HasAttrs + HasNodeId), + node: &mut impl InvocationCollectorNode, attr: ast::Attribute, pos: usize, ) -> EvalConfigResult { @@ -2219,8 +2304,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { &attr, attr.span, self.cfg().lint_node_id, - // Target doesn't matter for `cfg` parsing. - Target::Crate, + node.as_target(), self.cfg().features, ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, parse_cfg, diff --git a/tests/ui/cfg/suggest-any-or-all.stderr b/tests/ui/cfg/suggest-any-or-all.stderr index 67ff6f178d128..3d3bc7e051646 100644 --- a/tests/ui/cfg/suggest-any-or-all.stderr +++ b/tests/ui/cfg/suggest-any-or-all.stderr @@ -7,12 +7,12 @@ LL | #[cfg(foo, bar)] | expected a single argument here | = note: for more information, visit -help: if the crate should be enabled when all these predicates are, wrap them in `all` +help: if the function should be enabled when all these predicates are, wrap them in `all` | LL - #[cfg(foo, bar)] LL + #[cfg(all(foo, bar))] | -help: alternately, if the crate should be enabled when any of these predicates are, wrap them in `any` +help: alternately, if the function should be enabled when any of these predicates are, wrap them in `any` | LL - #[cfg(foo, bar)] LL + #[cfg(any(foo, bar))] @@ -27,7 +27,7 @@ LL | #[cfg()] | expected a single argument here | = note: for more information, visit -help: if the crate should be disabled, use `#[cfg(false)]` +help: if the struct should be disabled, use `#[cfg(false)]` | LL | #[cfg(false)] | +++++ diff --git a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr index ddb5cd84097f1..6187c36b0d6cb 100644 --- a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr +++ b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr @@ -29,7 +29,7 @@ LL | #[cfg()] | expected a single argument here | = note: for more information, visit -help: if the crate should be disabled, use `#[cfg(false)]` +help: if the struct should be disabled, use `#[cfg(false)]` | LL | #[cfg(false)] | +++++ @@ -43,12 +43,12 @@ LL | #[cfg(a, b)] | expected a single argument here | = note: for more information, visit -help: if the crate should be enabled when all these predicates are, wrap them in `all` +help: if the struct should be enabled when all these predicates are, wrap them in `all` | LL - #[cfg(a, b)] LL + #[cfg(all(a, b))] | -help: alternately, if the crate should be enabled when any of these predicates are, wrap them in `any` +help: alternately, if the struct should be enabled when any of these predicates are, wrap them in `any` | LL - #[cfg(a, b)] LL + #[cfg(any(a, b))] From 8c4480953c8d11883fab05555812594707632a19 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 2 Apr 2026 15:59:02 +0200 Subject: [PATCH 10/11] Correctly handle std "channels" in `rustdoc-js` tests --- src/tools/rustdoc-js/tester.js | 17 +++++++++++++---- tests/rustdoc-js/import-filter.js | 4 ++-- tests/rustdoc-js/renamed-crate-148300.js | 2 +- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index 124839efdd029..aaf40ceeadf71 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -4,6 +4,8 @@ const fs = require("fs"); const path = require("path"); const { isGeneratorObject } = require("util/types"); +const CHANNEL_REGEX = new RegExp("/nightly/|/beta/|/stable/|/1\\.[0-9]+\\.[0-9]+/"); + function arrayToCode(array) { return array.map((value, index) => { value = value.split(" ").join(" "); @@ -56,6 +58,8 @@ function valueMapper(key, testOutput) { value = testOutput["parent"]["name"]; } } + } else if (key === "href") { + value = value.replace(CHANNEL_REGEX, "/$CHANNEL/"); } return value; } @@ -69,13 +73,14 @@ function betterLookingDiff(expected, testOutput) { if (!Object.prototype.hasOwnProperty.call(expected, key)) { continue; } + const expectedValue = expected[key]; if (!testOutput || !Object.prototype.hasOwnProperty.call(testOutput, key)) { - output += "-" + spaces + contentToDiffLine(key, expected[key]) + "\n"; + output += "-" + spaces + contentToDiffLine(key, expectedValue) + "\n"; continue; } const value = valueMapper(key, testOutput); - if (value !== expected[key]) { - output += "-" + spaces + contentToDiffLine(key, expected[key]) + "\n"; + if (value !== expectedValue) { + output += "-" + spaces + contentToDiffLine(key, expectedValue) + "\n"; output += "+" + spaces + contentToDiffLine(key, value) + "\n"; } else { output += spaces + " " + contentToDiffLine(key, value) + "\n"; @@ -92,7 +97,11 @@ function lookForEntry(expected, testOutput) { continue; } const value = valueMapper(key, testOutputEntry); - if (value !== expected[key]) { + let expectedValue = expected[key]; + if (key === "href") { + expectedValue = expectedValue.replace(CHANNEL_REGEX, "/$CHANNEL/"); + } + if (value !== expectedValue) { allGood = false; break; } diff --git a/tests/rustdoc-js/import-filter.js b/tests/rustdoc-js/import-filter.js index ced41bd7e2a2b..6237ddb837974 100644 --- a/tests/rustdoc-js/import-filter.js +++ b/tests/rustdoc-js/import-filter.js @@ -9,7 +9,7 @@ const EXPECTED = [ { 'path': 'foo', 'name': 'st2', - 'href': 'https://doc.rust-lang.org/nightly/std/index.html' + 'href': 'https://doc.rust-lang.org/$CHANNEL/std/index.html' }, ], }, @@ -19,7 +19,7 @@ const EXPECTED = [ { 'path': 'foo', 'name': 'st2', - 'href': 'https://doc.rust-lang.org/nightly/std/index.html' + 'href': 'https://doc.rust-lang.org/$CHANNEL/std/index.html' }, ], }, diff --git a/tests/rustdoc-js/renamed-crate-148300.js b/tests/rustdoc-js/renamed-crate-148300.js index 9ac4e65631b77..fa3d64e77c5fd 100644 --- a/tests/rustdoc-js/renamed-crate-148300.js +++ b/tests/rustdoc-js/renamed-crate-148300.js @@ -6,7 +6,7 @@ const EXPECTED = [ { query: 'st2', others: [ - { name: 'st2', href: 'https://doc.rust-lang.org/nightly/std/index.html' } + { name: 'st2', href: 'https://doc.rust-lang.org/$CHANNEL/std/index.html' } ], }, ]; From edb93228ce910aa1073c42d4ef4807c983968df1 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Thu, 2 Apr 2026 13:52:23 +0000 Subject: [PATCH 11/11] fix doc in rustc_attr_parsing::context --- compiler/rustc_attr_parsing/src/context.rs | 34 +++++++++++++--------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 2bab0019eaf96..7bac0e90532ca 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -399,11 +399,11 @@ impl Stage for Late { } } -/// used when parsing attributes for miscellaneous things *before* ast lowering +/// Used when parsing attributes for miscellaneous things *before* ast lowering pub struct Early { - /// Whether to emit errors or delay them as a bug - /// For most attributes, the attribute will be parsed again in the `Late` stage and in this case the errors should be delayed - /// But for some, such as `cfg`, the attribute will be removed before the `Late` stage so errors must be emitted + /// Whether to emit errors or delay them as a bug. + /// For most attributes, the attribute will be parsed again in the `Late` stage and in this case the errors should be delayed. + /// But for some, such as `cfg`, the attribute will be removed before the `Late` stage so errors must be emitted. pub emit_errors: ShouldEmit, } /// used when parsing attributes during ast lowering @@ -416,19 +416,25 @@ pub struct AcceptContext<'f, 'sess, S: Stage> { pub(crate) shared: SharedContext<'f, 'sess, S>, /// The outer span of the attribute currently being parsed + /// + /// ```none /// #[attribute(...)] /// ^^^^^^^^^^^^^^^^^ outer span + /// ``` /// For attributes in `cfg_attr`, the outer span and inner spans are equal. pub(crate) attr_span: Span, - /// The inner span of the attribute currently being parsed + /// The inner span of the attribute currently being parsed. + /// + /// ```none /// #[attribute(...)] /// ^^^^^^^^^^^^^^ inner span + /// ``` pub(crate) inner_span: Span, - /// Whether it is an inner or outer attribute + /// Whether it is an inner or outer attribute. pub(crate) attr_style: AttrStyle, - /// A description of the thing we are parsing using this attribute parser + /// A description of the thing we are parsing using this attribute parser. /// We are not only using these parsers for attributes, but also for macros such as the `cfg!()` macro. pub(crate) parsed_description: ParsedDescription, @@ -715,12 +721,12 @@ where self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNoArgs) } - /// emit an error that a `name` was expected here + /// Emit an error that a `name` was expected here pub(crate) fn expected_identifier(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIdentifier) } - /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for + /// Emit an error that a `name = value` pair was expected at this span. The symbol can be given for /// a nicer error message talking about the specific name that was found lacking a value. pub(crate) fn expected_name_value( &mut self, @@ -730,12 +736,12 @@ where self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValue(name)) } - /// emit an error that a `name = value` pair was found where that name was already seen. + /// Emit an error that a `name = value` pair was found where that name was already seen. pub(crate) fn duplicate_key(&mut self, span: Span, key: Symbol) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::DuplicateKey(key)) } - /// an error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser) + /// An error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser) /// was expected *not* to be a literal, but instead a meta item. pub(crate) fn unexpected_literal(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::UnexpectedLiteral) @@ -749,7 +755,7 @@ where self.emit_parse_error(span, AttributeParseErrorReason::ExpectedAtLeastOneArgument) } - /// produces an error along the lines of `expected one of [foo, meow]` + /// Produces an error along the lines of `expected one of [foo, meow]` pub(crate) fn expected_specific_argument( &mut self, span: Span, @@ -765,7 +771,7 @@ where ) } - /// produces an error along the lines of `expected one of [foo, meow] as an argument`. + /// Produces an error along the lines of `expected one of [foo, meow] as an argument`. /// i.e. slightly different wording to [`expected_specific_argument`](Self::expected_specific_argument). pub(crate) fn expected_specific_argument_and_list( &mut self, @@ -830,7 +836,7 @@ where self.template.suggestions(style, &self.attr_path) } - /// error that a string literal was expected. + /// Error that a string literal was expected. /// You can optionally give the literal you did find (which you found not to be a string literal) /// which can make better errors. For example, if the literal was a byte string it will suggest /// removing the `b` prefix.