diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index c2dda74e9f515..49dd349b29fb7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -116,7 +116,7 @@ pub fn parse_cfg_entry( else { return Err(cx.adcx().expected_identifier(meta.path().span())); }; - parse_name_value(name, meta.path().span(), a.name_value(), meta.span(), cx)? + parse_name_value(name, meta.path().span(), a.as_name_value(), meta.span(), cx)? } }, MetaItemOrLitParser::Lit(lit) => match lit.kind { @@ -178,23 +178,16 @@ fn parse_cfg_entry_target( let mut result = ThinVec::new(); for sub_item in list.mixed() { // First, validate that this is a NameValue item - let Some(sub_item) = sub_item.meta_item() else { - cx.adcx().expected_name_value(sub_item.span(), None); - continue; - }; - let Some(nv) = sub_item.args().name_value() else { - cx.adcx().expected_name_value(sub_item.span(), None); + let Some((name, value)) = cx.expect_name_value(sub_item, sub_item.span(), None) else { 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.adcx().expected_identifier(sub_item.path().span())); - }; + if name.is_path_segment_keyword() { + return Err(cx.adcx().expected_identifier(name.span)); + } let name = Symbol::intern(&format!("target_{name}")); - if let Ok(cfg) = - parse_name_value(name, sub_item.path().span(), Some(nv), sub_item.span(), cx) - { + if let Ok(cfg) = parse_name_value(name, sub_item.span(), Some(value), sub_item.span(), cx) { result.push(cfg); } } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs b/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs index 32ea506211c91..4c2476b6b4f11 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs @@ -11,11 +11,7 @@ impl SingleAttributeParser for CfiEncodingParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "encoding"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(name_value) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, Some(sym::cfi_encoding)); - return None; - }; + let name_value = cx.expect_name_value(args, cx.attr_span, Some(sym::cfi_encoding))?; let Some(value_str) = name_value.value_as_str() else { cx.adcx().expected_string_literal(name_value.value_span, None); diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index ceb4da3df6a22..f2963963d572f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -118,11 +118,7 @@ impl SingleAttributeParser for ExportNameParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(name) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; @@ -146,11 +142,7 @@ impl SingleAttributeParser for RustcObjcClassParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(classname) = nv.value_as_str() else { // `#[rustc_objc_class = ...]` is expected to be used as an implementation detail // inside a standard library macro, but `cx.expected_string_literal` exposes too much. @@ -177,11 +169,7 @@ impl SingleAttributeParser for RustcObjcSelectorParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(methname) = nv.value_as_str() else { // `#[rustc_objc_selector = ...]` is expected to be used as an implementation detail // inside a standard library macro, but `cx.expected_string_literal` exposes too much. @@ -471,29 +459,20 @@ fn parse_tf_attribute( return features; } for item in list.mixed() { - let Some(name_value) = item.meta_item() else { - cx.adcx().expected_name_value(item.span(), Some(sym::enable)); + let Some((ident, value)) = cx.expect_name_value(item, item.span(), Some(sym::enable)) + else { return features; }; // Validate name - let Some(name) = name_value.path().word_sym() else { - cx.adcx().expected_name_value(name_value.path().span(), Some(sym::enable)); - return features; - }; - if name != sym::enable { - cx.adcx().expected_name_value(name_value.path().span(), Some(sym::enable)); + if ident.name != sym::enable { + cx.adcx().expected_specific_argument(ident.span, &[sym::enable]); return features; } // Use value - let Some(name_value) = name_value.args().name_value() else { - cx.adcx().expected_name_value(item.span(), Some(sym::enable)); - return features; - }; - let Some(value_str) = name_value.value_as_str() else { - cx.adcx() - .expected_string_literal(name_value.value_span, Some(name_value.value_as_lit())); + let Some(value_str) = value.value_as_str() else { + cx.adcx().expected_string_literal(value.value_span, Some(value.value_as_lit())); return features; }; for feature in value_str.as_str().split(",") { @@ -592,14 +571,7 @@ impl SingleAttributeParser for SanitizeParser { let mut rtsan = None; for item in list.mixed() { - let Some(item) = item.meta_item() else { - cx.adcx().expected_name_value(item.span(), None); - continue; - }; - - let path = item.path().word_sym(); - let Some(value) = item.args().name_value() else { - cx.adcx().expected_name_value(item.span(), path); + let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else { continue; }; @@ -628,20 +600,20 @@ impl SingleAttributeParser for SanitizeParser { } }; - match path { - Some(sym::address) | Some(sym::kernel_address) => { + match ident.name { + sym::address | sym::kernel_address => { apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS) } - Some(sym::cfi) => apply(SanitizerSet::CFI), - Some(sym::kcfi) => apply(SanitizerSet::KCFI), - Some(sym::memory) => apply(SanitizerSet::MEMORY), - Some(sym::memtag) => apply(SanitizerSet::MEMTAG), - Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK), - Some(sym::thread) => apply(SanitizerSet::THREAD), - Some(sym::hwaddress) | Some(sym::kernel_hwaddress) => { + sym::cfi => apply(SanitizerSet::CFI), + sym::kcfi => apply(SanitizerSet::KCFI), + sym::memory => apply(SanitizerSet::MEMORY), + sym::memtag => apply(SanitizerSet::MEMTAG), + sym::shadow_call_stack => apply(SanitizerSet::SHADOWCALLSTACK), + sym::thread => apply(SanitizerSet::THREAD), + sym::hwaddress | sym::kernel_hwaddress => { apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS) } - Some(sym::realtime) => match value.value_as_str() { + sym::realtime => match value.value_as_str() { Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking), Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking), Some(sym::caller) => rtsan = Some(RtsanSetting::Caller), @@ -654,7 +626,7 @@ impl SingleAttributeParser for SanitizeParser { }, _ => { cx.adcx().expected_specific_argument_strings( - item.path().span(), + ident.span, &[ sym::address, sym::kernel_address, @@ -725,33 +697,25 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { let mut errored = false; for item in meta_item_list.mixed() { - let Some(meta_item) = item.meta_item() else { - errored = true; - cx.adcx().expected_name_value(item.span(), None); - continue; - }; - - let Some(name_value_lit) = meta_item.args().name_value() else { - errored = true; - cx.adcx().expected_name_value(item.span(), None); + let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else { continue; }; - let attrib_to_write = match meta_item.ident().map(|ident| ident.name) { - Some(sym::prefix_nops) => { + let attrib_to_write = match ident.name { + sym::prefix_nops => { // Duplicate prefixes are not allowed if prefix.is_some() { errored = true; - cx.adcx().duplicate_key(meta_item.path().span(), sym::prefix_nops); + cx.adcx().duplicate_key(ident.span, sym::prefix_nops); continue; } &mut prefix } - Some(sym::entry_nops) => { + sym::entry_nops => { // Duplicate entries are not allowed if entry.is_some() { errored = true; - cx.adcx().duplicate_key(meta_item.path().span(), sym::entry_nops); + cx.adcx().duplicate_key(ident.span, sym::entry_nops); continue; } &mut entry @@ -759,23 +723,23 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { _ => { errored = true; cx.adcx().expected_specific_argument( - meta_item.path().span(), + ident.span, &[sym::prefix_nops, sym::entry_nops], ); continue; } }; - let rustc_ast::LitKind::Int(val, _) = name_value_lit.value_as_lit().kind else { + let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else { errored = true; - cx.adcx().expected_integer_literal(name_value_lit.value_span); + cx.adcx().expected_integer_literal(value.value_span); continue; }; let Ok(val) = val.get().try_into() else { errored = true; cx.adcx().expected_integer_literal_in_range( - name_value_lit.value_span, + value.value_span, u8::MIN as isize, u8::MAX as isize, ); diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 451e126dd5c6a..6f2dbb117e2a1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -16,11 +16,7 @@ impl SingleAttributeParser for CrateNameParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let ArgParser::NameValue(n) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let n = cx.expect_name_value(args, cx.attr_span, None)?; let Some(name) = n.value_as_str() else { cx.adcx().expected_string_literal(n.value_span, Some(n.value_as_lit())); @@ -47,11 +43,7 @@ impl CombineAttributeParser for CrateTypeParser { cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser, ) -> impl IntoIterator { - let ArgParser::NameValue(n) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let n = cx.expect_name_value(args, cx.attr_span, None)?; let Some(crate_type) = n.value_as_str() else { cx.adcx().expected_string_literal(n.value_span, Some(n.value_as_lit())); @@ -95,11 +87,7 @@ impl SingleAttributeParser for RecursionLimitParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let ArgParser::NameValue(nv) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; Some(AttributeKind::RecursionLimit { limit: cx.parse_limit_int(nv)?, @@ -117,11 +105,7 @@ impl SingleAttributeParser for MoveSizeLimitParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let ArgParser::NameValue(nv) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; Some(AttributeKind::MoveSizeLimit { limit: cx.parse_limit_int(nv)?, @@ -140,11 +124,7 @@ impl SingleAttributeParser for TypeLengthLimitParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let ArgParser::NameValue(nv) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; Some(AttributeKind::TypeLengthLimit { limit: cx.parse_limit_int(nv)?, @@ -162,11 +142,7 @@ impl SingleAttributeParser for PatternComplexityLimitParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let ArgParser::NameValue(nv) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; Some(AttributeKind::PatternComplexityLimit { limit: cx.parse_limit_int(nv)?, @@ -219,14 +195,7 @@ impl SingleAttributeParser for WindowsSubsystemParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: ["windows", "console"], "https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let inner_span = cx.inner_span; - cx.adcx().expected_name_value( - args.span().unwrap_or(inner_span), - Some(sym::windows_subsystem), - ); - return None; - }; + let nv = cx.expect_name_value(args, cx.inner_span, Some(sym::windows_subsystem))?; let kind = match nv.value_as_str() { Some(sym::console) => WindowsSubsystemKind::Console, diff --git a/compiler/rustc_attr_parsing/src/attributes/debugger.rs b/compiler/rustc_attr_parsing/src/attributes/debugger.rs index f31cf70085442..0c137a55d4cfd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/debugger.rs +++ b/compiler/rustc_attr_parsing/src/attributes/debugger.rs @@ -21,33 +21,24 @@ impl CombineAttributeParser for DebuggerViualizerParser { args: &ArgParser, ) -> impl IntoIterator { let single = cx.expect_single_element_list(args, cx.attr_span)?; - let Some(mi) = single.meta_item() else { - cx.adcx().expected_name_value(single.span(), None); - return None; - }; - let path = mi.path().word_sym(); - let visualizer_type = match path { - Some(sym::natvis_file) => DebuggerVisualizerType::Natvis, - Some(sym::gdb_script_file) => DebuggerVisualizerType::GdbPrettyPrinter, + let (ident, args) = cx.expect_name_value(single, single.span(), None)?; + let visualizer_type = match ident.name { + sym::natvis_file => DebuggerVisualizerType::Natvis, + sym::gdb_script_file => DebuggerVisualizerType::GdbPrettyPrinter, _ => { cx.adcx().expected_specific_argument( - mi.path().span(), + ident.span, &[sym::natvis_file, sym::gdb_script_file], ); return None; } }; - let Some(path) = mi.args().name_value() else { - cx.adcx().expected_name_value(single.span(), path); - return None; - }; - - let Some(path) = path.value_as_str() else { - cx.adcx().expected_string_literal(path.value_span, Some(path.value_as_lit())); + let Some(path) = args.value_as_str() else { + cx.adcx().expected_string_literal(args.value_span, Some(args.value_as_lit())); return None; }; - Some(DebugVisualizer { span: mi.span(), visualizer_type, path }) + Some(DebugVisualizer { span: ident.span.to(args.value_span), visualizer_type, path }) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index e948d120fafb3..c24b50b439ece 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -18,15 +18,11 @@ fn get( 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.adcx().expected_string_literal(v.value_span, Some(&v.value_as_lit())); - None - } + let v = cx.expect_name_value(arg, param_span, Some(name))?; + if let Some(value_str) = v.value_as_ident() { + Some(value_str) } else { - cx.adcx().expected_name_value(param_span, Some(name)); + cx.adcx().expected_string_literal(v.value_span, Some(&v.value_as_lit())); None } } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index e56ed166592aa..dbdf436bd8edc 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -267,7 +267,7 @@ fn parse_directive_items<'p, S: Stage>( // But we don't assert its presence yet because we don't want to mention it // if someone does something like `#[diagnostic::on_unimplemented(doesnt_exist)]`. // That happens in the big `match` below. - let value: Option = match item.args().name_value() { + let value: Option = match item.args().as_name_value() { Some(nv) => Some(or_malformed!(nv.value_as_ident()?)), None => None, }; diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 5d07478b152ee..2267ea9d27656 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -121,7 +121,7 @@ fn parse_keyword_and_attribute( attr_value: &mut Option<(Symbol, Span)>, attr_name: Symbol, ) { - let Some(nv) = args.name_value() else { + let Some(nv) = args.as_name_value() else { expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym()); return; }; @@ -410,7 +410,7 @@ impl DocParser { super::cfg::parse_name_value( name, sub_item.path().span(), - a.name_value(), + a.as_name_value(), sub_item.span(), cx, ) @@ -421,7 +421,7 @@ impl DocParser { // If `value` is `Some`, `a.name_value()` will always return // `Some` as well. value: value - .map(|v| (v, a.name_value().unwrap().value_span)), + .map(|v| (v, a.as_name_value().unwrap().value_span)), }) } } @@ -515,7 +515,7 @@ impl DocParser { } macro_rules! string_arg_and_crate_level { ($ident: ident) => {{ - let Some(nv) = args.name_value() else { + let Some(nv) = args.as_name_value() else { expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym()); return; }; @@ -622,7 +622,7 @@ impl DocParser { span, ); } - Some(sym::include) if let Some(nv) = args.name_value() => { + Some(sym::include) if let Some(nv) = args.as_name_value() => { let inner = match cx.attr_style { AttrStyle::Outer => "", AttrStyle::Inner => "!", diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 105fe77eba73a..14d3d45bf68f7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -36,11 +36,7 @@ impl SingleAttributeParser for LinkNameParser { ); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(name) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; @@ -261,12 +257,11 @@ impl LinkParser { cx.adcx().duplicate_key(item.span(), sym::name); return true; } - let Some(nv) = item.args().name_value() else { - cx.adcx().expected_name_value(item.span(), Some(sym::name)); + let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::name)) else { return false; }; let Some(link_name) = nv.value_as_str() else { - cx.adcx().expected_name_value(item.span(), Some(sym::name)); + cx.adcx().expected_string_literal(nv.args_span(), Some(nv.value_as_lit())); return false; }; @@ -288,12 +283,11 @@ impl LinkParser { cx.adcx().duplicate_key(item.span(), sym::kind); return true; } - let Some(nv) = item.args().name_value() else { - cx.adcx().expected_name_value(item.span(), Some(sym::kind)); + let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::kind)) else { return true; }; let Some(link_kind) = nv.value_as_str() else { - cx.adcx().expected_name_value(item.span(), Some(sym::kind)); + cx.adcx().expected_string_literal(item.span(), Some(nv.value_as_lit())); return true; }; @@ -368,12 +362,11 @@ impl LinkParser { cx.adcx().duplicate_key(item.span(), sym::modifiers); return true; } - let Some(nv) = item.args().name_value() else { - cx.adcx().expected_name_value(item.span(), Some(sym::modifiers)); + let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::modifiers)) else { return true; }; let Some(link_modifiers) = nv.value_as_str() else { - cx.adcx().expected_name_value(item.span(), Some(sym::modifiers)); + cx.adcx().expected_string_literal(item.span(), Some(nv.value_as_lit())); return true; }; *modifiers = Some((link_modifiers, nv.value_span)); @@ -410,12 +403,13 @@ impl LinkParser { cx.adcx().duplicate_key(item.span(), sym::wasm_import_module); return true; } - let Some(nv) = item.args().name_value() else { - cx.adcx().expected_name_value(item.span(), Some(sym::wasm_import_module)); + let Some(nv) = + cx.expect_name_value(item.args(), item.span(), Some(sym::wasm_import_module)) + else { return true; }; let Some(link_wasm_import_module) = nv.value_as_str() else { - cx.adcx().expected_name_value(item.span(), Some(sym::wasm_import_module)); + cx.adcx().expected_string_literal(item.span(), Some(nv.value_as_lit())); return true; }; *wasm_import_module = Some((link_wasm_import_module, item.span())); @@ -431,12 +425,12 @@ impl LinkParser { cx.adcx().duplicate_key(item.span(), sym::import_name_type); return true; } - let Some(nv) = item.args().name_value() else { - cx.adcx().expected_name_value(item.span(), Some(sym::import_name_type)); + let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::import_name_type)) + else { return true; }; let Some(link_import_name_type) = nv.value_as_str() else { - cx.adcx().expected_name_value(item.span(), Some(sym::import_name_type)); + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return true; }; if cx.sess().target.arch != Arch::X86 { @@ -503,11 +497,7 @@ impl SingleAttributeParser for LinkSectionParser { ); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(name) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; @@ -638,11 +628,7 @@ impl SingleAttributeParser for LinkageParser { ]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(name_value) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, Some(sym::linkage)); - return None; - }; + let name_value = cx.expect_name_value(args, cx.attr_span, Some(sym::linkage))?; let Some(value) = name_value.value_as_str() else { cx.adcx() diff --git a/compiler/rustc_attr_parsing/src/attributes/path.rs b/compiler/rustc_attr_parsing/src/attributes/path.rs index 08daa4aa5249b..165425b11fa74 100644 --- a/compiler/rustc_attr_parsing/src/attributes/path.rs +++ b/compiler/rustc_attr_parsing/src/attributes/path.rs @@ -13,11 +13,7 @@ impl SingleAttributeParser for PathParser { ); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(path) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs index 1778c68832a3c..b18f4460f3f82 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prototype.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs @@ -7,7 +7,7 @@ use rustc_span::{Span, Symbol, sym}; use crate::attributes::SingleAttributeParser; use crate::context::{AcceptContext, Stage}; -use crate::parser::ArgParser; +use crate::parser::{ArgParser, NameValueParser}; use crate::session_diagnostics; use crate::target_checking::AllowedTargets; use crate::target_checking::Policy::Allow; @@ -29,23 +29,23 @@ impl SingleAttributeParser for CustomMirParser { let mut failed = false; for item in list.mixed() { - let Some(meta_item) = item.meta_item() else { - cx.adcx().expected_name_value(item.span(), None); + let Some((path, arg)) = cx.expect_name_value(item, item.span(), None) else { failed = true; break; }; - if let Some(arg) = meta_item.word_is(sym::dialect) { - extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed); - } 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.adcx().expected_specific_argument(meta_item.span(), &[sym::dialect, sym::phase]); - failed = true; - } else { - cx.adcx().expected_name_value(meta_item.span(), None); - failed = true; - }; + match path.name { + sym::dialect => { + extract_value(cx, sym::dialect, arg, item.span(), &mut dialect, &mut failed) + } + sym::phase => { + extract_value(cx, sym::phase, arg, item.span(), &mut phase, &mut failed) + } + _ => { + cx.adcx().expected_specific_argument(item.span(), &[sym::dialect, sym::phase]); + failed = true; + } + } } let dialect = parse_dialect(cx, dialect, &mut failed); @@ -63,7 +63,7 @@ impl SingleAttributeParser for CustomMirParser { fn extract_value( cx: &mut AcceptContext<'_, '_, S>, key: Symbol, - arg: &ArgParser, + val: &NameValueParser, span: Span, out_val: &mut Option<(Symbol, Span)>, failed: &mut bool, @@ -74,12 +74,6 @@ fn extract_value( return; } - let Some(val) = arg.name_value() else { - cx.adcx().expected_name_value(span, Some(key)); - *failed = true; - return; - }; - let Some(value_sym) = val.value_as_str() else { cx.adcx().expected_string_literal(val.value_span, Some(val.value_as_lit())); *failed = true; diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs index 9590a23ae9341..a37eb036b6159 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_allocator.rs @@ -26,9 +26,9 @@ impl SingleAttributeParser for RustcAllocatorZeroedVariantParser { AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]); 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 { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); + let nv = cx.expect_name_value(args, cx.attr_span, None)?; + let Some(name) = nv.value_as_str() else { + cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); 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 1d35923339eb7..4904b00c6dfde 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -209,18 +209,17 @@ fn parse_cgu_fields( let mut kind = None::<(Symbol, Span)>; for arg in args.mixed() { - let Some(arg) = arg.meta_item() else { - cx.adcx().expected_name_value(args.span, None); + let Some((ident, arg)) = cx.expect_name_value(arg, arg.span(), None) else { continue; }; - let res = match arg.ident().map(|i| i.name) { - Some(sym::cfg) => &mut cfg, - Some(sym::module) => &mut module, - Some(sym::kind) if accepts_kind => &mut kind, + let res = match ident.name { + sym::cfg => &mut cfg, + sym::module => &mut module, + sym::kind if accepts_kind => &mut kind, _ => { cx.adcx().expected_specific_argument( - arg.path().span(), + ident.span, if accepts_kind { &[sym::cfg, sym::module, sym::kind] } else { @@ -231,22 +230,17 @@ fn parse_cgu_fields( } }; - let Some(i) = arg.args().name_value() else { - cx.adcx().expected_name_value(arg.span(), None); - continue; - }; - - let Some(str) = i.value_as_str() else { - cx.adcx().expected_string_literal(i.value_span, Some(i.value_as_lit())); + let Some(str) = arg.value_as_str() else { + cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit())); continue; }; if res.is_some() { - cx.adcx().duplicate_key(arg.span(), arg.ident().unwrap().name); + cx.adcx().duplicate_key(ident.span.to(arg.args_span()), ident.name); continue; } - *res = Some((str, i.value_span)); + *res = Some((str, arg.value_span)); } let Some((cfg, _)) = cfg else { @@ -349,23 +343,15 @@ impl SingleAttributeParser for RustcDeprecatedSafe2024Parser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let single = cx.expect_single_element_list(args, cx.attr_span)?; - let Some(arg) = single.meta_item() else { - cx.adcx().expected_name_value(single.span(), None); - return None; - }; - - let Some(args) = arg.word_is(sym::audit_that) else { - cx.adcx().expected_specific_argument(arg.span(), &[sym::audit_that]); - return None; - }; + let (path, arg) = cx.expect_name_value(single, cx.attr_span, None)?; - let Some(nv) = args.name_value() else { - cx.adcx().expected_name_value(arg.span(), Some(sym::audit_that)); + if path.name != sym::audit_that { + cx.adcx().expected_specific_argument(path.span, &[sym::audit_that]); return None; }; - let Some(suggestion) = nv.value_as_str() else { - cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + let Some(suggestion) = arg.value_as_str() else { + cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit())); return None; }; @@ -412,39 +398,33 @@ impl SingleAttributeParser for RustcNeverTypeOptionsParser { let mut diverging_block_default = None::; for arg in list.mixed() { - let Some(meta) = arg.meta_item() else { - cx.adcx().expected_name_value(arg.span(), None); + let Some((ident, arg)) = cx.expect_name_value(arg, arg.span(), None) else { continue; }; - let res = match meta.ident().map(|i| i.name) { - Some(sym::fallback) => &mut fallback, - Some(sym::diverging_block_default) => &mut diverging_block_default, + let res = match ident.name { + sym::fallback => &mut fallback, + sym::diverging_block_default => &mut diverging_block_default, _ => { cx.adcx().expected_specific_argument( - meta.path().span(), + ident.span, &[sym::fallback, sym::diverging_block_default], ); continue; } }; - let Some(nv) = meta.args().name_value() else { - cx.adcx().expected_name_value(meta.span(), None); - continue; - }; - - let Some(field) = nv.value_as_str() else { - cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + let Some(field) = arg.value_as_str() else { + cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit())); continue; }; if res.is_some() { - cx.adcx().duplicate_key(meta.span(), meta.ident().unwrap().name); + cx.adcx().duplicate_key(ident.span, ident.name); continue; } - *res = Some(Ident { name: field, span: nv.value_span }); + *res = Some(Ident { name: field, span: arg.value_span }); } let fallback = match fallback { @@ -562,11 +542,7 @@ impl SingleAttributeParser for RustcSimdMonomorphizeLaneLimitParser const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let ArgParser::NameValue(nv) = args else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; Some(AttributeKind::RustcSimdMonomorphizeLaneLimit(cx.parse_limit_int(nv)?)) } } @@ -603,11 +579,7 @@ impl SingleAttributeParser for LangParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(name) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; @@ -701,13 +673,11 @@ impl CombineAttributeParser for RustcMirParser { sym::rustc_peek_liveness => Some(RustcMirKind::PeekLiveness), sym::stop_after_dataflow => Some(RustcMirKind::StopAfterDataflow), sym::borrowck_graphviz_postflow => { - let Some(nv) = mi.args().name_value() else { - cx.adcx().expected_name_value( - mi.span(), - Some(sym::borrowck_graphviz_postflow), - ); - return None; - }; + let nv = cx.expect_name_value( + mi.args(), + mi.span(), + Some(sym::borrowck_graphviz_postflow), + )?; let Some(path) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, None); return None; @@ -721,13 +691,11 @@ impl CombineAttributeParser for RustcMirParser { } } sym::borrowck_graphviz_format => { - let Some(nv) = mi.args().name_value() else { - cx.adcx().expected_name_value( - mi.span(), - Some(sym::borrowck_graphviz_format), - ); - return None; - }; + let nv = cx.expect_name_value( + mi.args(), + mi.span(), + Some(sym::borrowck_graphviz_format), + )?; let Some(format) = nv.value_as_ident() else { cx.adcx().expected_identifier(nv.value_span); return None; @@ -814,10 +782,7 @@ impl CombineAttributeParser for RustcCleanParser { let mut cfg = None; for item in list.mixed() { - let Some((value, name)) = - item.meta_item().and_then(|m| Option::zip(m.args().name_value(), m.ident())) - else { - cx.adcx().expected_name_value(item.span(), None); + let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else { continue; }; let value_span = value.value_span; @@ -825,11 +790,10 @@ impl CombineAttributeParser for RustcCleanParser { cx.adcx().expected_string_literal(value_span, None); continue; }; - match name.name { + match ident.name { sym::cfg if cfg.is_some() => { cx.adcx().duplicate_key(item.span(), sym::cfg); } - sym::cfg => { cfg = Some(value); } @@ -851,7 +815,7 @@ impl CombineAttributeParser for RustcCleanParser { } _ => { cx.adcx().expected_specific_argument( - name.span, + ident.span, &[sym::cfg, sym::except, sym::loaded_from_disk], ); } @@ -1047,11 +1011,7 @@ impl SingleAttributeParser for RustcDiagnosticItemParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(value) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; @@ -1106,11 +1066,7 @@ impl SingleAttributeParser for RustcReservationImplParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "reservation message"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(args.span().unwrap_or(attr_span), None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(value_str) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); @@ -1137,11 +1093,7 @@ impl SingleAttributeParser for RustcDocPrimitiveParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "primitive name"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let span = cx.attr_span; - cx.adcx().expected_name_value(args.span().unwrap_or(span), None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; let Some(value_str) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index f6f964fb4d69e..9411d06dd8f67 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -102,9 +102,7 @@ impl AttributeParser for StabilityParser { template!(NameValueStr: "deprecation message"), |this, cx, args| { reject_outside_std!(cx); - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); + let Some(nv) = cx.expect_name_value(args, cx.attr_span, None) else { return; }; let Some(value_str) = nv.value_as_str() else { @@ -290,16 +288,19 @@ fn insert_value_into_option_or_error( ) -> Option<()> { if item.is_some() { 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() - { - *item = Some(s); - Some(()) - } else { - cx.adcx().expected_name_value(param.span(), Some(name.name)); - None + return None; } + + let (_ident, arg) = cx.expect_name_value(param, param.span(), Some(name.name))?; + + let Some(s) = arg.value_as_str() else { + cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit())); + return None; + }; + + *item = Some(s); + + Some(()) } /// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and @@ -409,7 +410,7 @@ pub(crate) fn parse_unstability( session_diagnostics::InvalidIssueString { span: param.span(), cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind( - param.args().name_value().unwrap().value_span, + param.args().as_name_value().unwrap().value_span, err.kind(), ), }, diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index 456b29d0c3aa3..4101a5a6427b5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -73,20 +73,14 @@ impl SingleAttributeParser for ShouldPanicParser { } ArgParser::List(list) => { let single = cx.expect_single(list)?; - let Some(single) = single.meta_item() else { - cx.adcx().expected_name_value(single.span(), Some(sym::expected)); - return None; - }; - if !single.path().word_is(sym::expected) { + let (ident, arg) = + cx.expect_name_value(single, single.span(), Some(sym::expected))?; + if ident.name != sym::expected { cx.adcx().expected_specific_argument_strings(list.span, &[sym::expected]); return None; } - let Some(nv) = single.args().name_value() else { - cx.adcx().expected_name_value(single.span(), Some(sym::expected)); - return None; - }; - let Some(expected) = nv.value_as_str() else { - cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + let Some(expected) = arg.value_as_str() else { + cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit())); return None; }; Some(expected) @@ -104,14 +98,11 @@ impl SingleAttributeParser for ReexportTestHarnessMainParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - 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 nv = cx.expect_name_value( + args, + args.span().unwrap_or(cx.inner_span), + Some(sym::reexport_test_harness_main), + )?; let Some(name) = nv.value_as_str() else { cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit())); @@ -220,11 +211,7 @@ impl SingleAttributeParser for RustcTestMarkerParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "test_path"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(name_value) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, Some(sym::rustc_test_marker)); - return None; - }; + let name_value = cx.expect_name_value(args, cx.attr_span, Some(sym::rustc_test_marker))?; let Some(value_str) = name_value.value_as_str() else { cx.adcx().expected_string_literal(name_value.value_span, None); diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index 5685a2eccb35d..d6739cf3fa7f2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -14,11 +14,7 @@ impl SingleAttributeParser for RustcMacroTransparencyParser { template!(NameValueStr: ["transparent", "semiopaque", "opaque"]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { - let Some(nv) = args.name_value() else { - let attr_span = cx.attr_span; - cx.adcx().expected_name_value(attr_span, None); - return None; - }; + let nv = cx.expect_name_value(args, cx.attr_span, None)?; match nv.value_as_str() { Some(sym::transparent) => Some(Transparency::Transparent), Some(sym::semiopaque) => Some(Transparency::SemiOpaque), diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index cebbabfcbf1be..d3ba2ea10ed85 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -16,7 +16,7 @@ use rustc_hir::{AttrPath, HirId}; use rustc_parse::parser::Recovery; use rustc_session::Session; use rustc_session::lint::{Lint, LintId}; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; // Glob imports to avoid big, bitrotty import lists use crate::attributes::allow_unstable::*; @@ -61,7 +61,10 @@ use crate::attributes::test_attrs::*; use crate::attributes::traits::*; use crate::attributes::transparency::*; use crate::attributes::{AttributeParser as _, AttributeSafety, Combine, Single, WithoutArgs}; -use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, RefPathParser}; +use crate::parser::{ + ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, NameValueParser, + RefPathParser, +}; use crate::session_diagnostics::{ AttributeParseError, AttributeParseErrorReason, AttributeParseErrorSuggestions, ParsedDescription, @@ -580,7 +583,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { /// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list /// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list /// - /// This is a higher-level (and harder to misuse) wrapper over [`ArgParser::as_list`]. That + /// This is a higher-level (and harder to misuse) wrapper over [`ArgParser::as_list`] that /// allows using `?` when the attribute parsing function allows it. You may still want to use /// [`ArgParser::as_list`] for the following reasons: /// @@ -601,8 +604,8 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { /// Asserts that a [`MetaItemListParser`] contains a single element and returns it, or emits an /// error and returns `None`. /// - /// This is a higher-level (and harder to misuse) wrapper over [`MetaItemListParser::as_single`]. - /// That allows using `?` to early return. You may still want to use + /// This is a higher-level (and harder to misuse) wrapper over [`MetaItemListParser::as_single`], + /// that allows using `?` to early return. You may still want to use /// [`MetaItemListParser::as_single`] for the following reasons: /// /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). @@ -617,6 +620,134 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { } single } + + /// Asserts that a node is a name-value pair. + /// + /// Some examples: + /// + /// - `#[clippy::cyclomatic_complexity = "100"]`: `clippy::cyclomatic_complexity = "100"` is a + /// name-value pair, where the name is a path (`clippy::cyclomatic_complexity`). You already + /// checked the path to get an `ArgParser`, so this method will effectively only assert that + /// the `= "100"` is there and returns it. + /// - `#[doc = "hello"]`: `doc = "hello` is also a name value pair. `= "hello"` is returned. + /// - `#[serde(rename_all = "lowercase")]`: `rename_all = "lowercase"` is a name value pair, + /// where the name is an identifier (`rename_all`) and the value is a literal (`"lowercase"`). + /// This returns both the path and the value. + /// + /// `arg` must be a reference to any node that may contain a name-value pair, that is: + /// + /// - [`MetaItemOrLitParser`], + /// - [`MetaItemParser`], + /// - [`ArgParser`]. + /// + /// `name` can be set to `Some` for a nicer error message talking about the specific name that + /// was found lacking a value. + /// + /// This is a higher-level (and harder to misuse) wrapper over multiple `as_` methods in the + /// [`parser`][crate::parser] module. You may still want to use the lower-level methods for the + /// following reasons: + /// + /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). + /// - The attribute can be parsed in multiple ways and it does not make sense to emit an error. + pub(crate) fn expect_name_value<'arg, Arg>( + &mut self, + arg: &'arg Arg, + span: Span, + name: Option, + ) -> Option> + where + Arg: ExpectNameValue, + { + arg.expect_name_value(self, span, name) + } +} + +pub(crate) trait ExpectNameValue { + type Output<'a> + where + Self: 'a; + + fn expect_name_value<'a, 'f, 'sess, S>( + &'a self, + cx: &mut AcceptContext<'f, 'sess, S>, + span: Span, + name: Option, + ) -> Option> + where + S: Stage; +} + +impl ExpectNameValue for MetaItemOrLitParser { + type Output<'a> = (Ident, &'a NameValueParser); + + fn expect_name_value<'a, 'f, 'sess, S>( + &'a self, + cx: &mut AcceptContext<'f, 'sess, S>, + span: Span, + name: Option, + ) -> Option> + where + S: Stage, + { + let Some(meta_item) = self.meta_item() else { + cx.adcx().expected_name_value(self.span(), name); + return None; + }; + + meta_item.expect_name_value(cx, span, name) + } +} + +impl ExpectNameValue for MetaItemParser { + type Output<'a> = (Ident, &'a NameValueParser); + + fn expect_name_value<'a, 'f, 'sess, S>( + &'a self, + cx: &mut AcceptContext<'f, 'sess, S>, + _span: Span, // Not needed: `MetaItemOrLitParser` carry its own span. + name: Option, + ) -> Option> + where + S: Stage, + { + let word = self.path().word(); + let arg = self.args().as_name_value(); + + if word.is_none() { + cx.adcx().expected_identifier(self.path().span()); + } + + if arg.is_none() { + cx.adcx().expected_name_value(self.span(), name); + } + + let Some((word, arg)) = word.zip(arg) else { + return None; + }; + + Some((word, arg)) + } +} + +impl ExpectNameValue for ArgParser { + type Output<'a> = &'a NameValueParser; + + fn expect_name_value<'a, 'f, 'sess, S>( + &'a self, + cx: &mut AcceptContext<'f, 'sess, S>, + span: Span, + name: Option, + ) -> Option> + where + S: Stage, + { + let Some(nv) = self.as_name_value() else { + cx.adcx().expected_name_value(span, name); + return None; + }; + + Some(nv) + } } impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> { @@ -849,11 +980,7 @@ where /// 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, - span: Span, - name: Option, - ) -> ErrorGuaranteed { + fn expected_name_value(&mut self, span: Span, name: Option) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValue(name)) } diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index ce2367006128d..de505cc8c7ac8 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -192,7 +192,7 @@ impl ArgParser { /// to get an `ArgParser`, so this method will effectively only assert that the `= "100"` is /// there /// - `#[doc = "hello"]`: `doc = "hello` is also a name value pair - pub fn name_value(&self) -> Option<&NameValueParser> { + pub fn as_name_value(&self) -> Option<&NameValueParser> { match self { Self::NameValue(n) => Some(n), Self::List(_) | Self::NoArgs => None, @@ -264,8 +264,9 @@ impl MetaItemOrLitParser { /// MetaItems consist of some path, and some args. The args could be empty. In other words: /// /// - `name` -> args are empty -/// - `name(...)` -> args are a [`list`](ArgParser::as_list), which is the bit between the parentheses -/// - `name = value`-> arg is [`name_value`](ArgParser::name_value), where the argument is the +/// - `name(...)` -> args are a [`list`](ArgParser::as_list), which is the bit between the +/// parentheses +/// - `name = value`-> arg is [`name_value`](ArgParser::as_name_value), where the argument is the /// `= value` part /// /// The syntax of MetaItems can be found at diff --git a/tests/ui/cfg/cfg-target-compact-errors.rs b/tests/ui/cfg/cfg-target-compact-errors.rs index 1ce68330c7623..c674232d1d373 100644 --- a/tests/ui/cfg/cfg-target-compact-errors.rs +++ b/tests/ui/cfg/cfg-target-compact-errors.rs @@ -4,22 +4,35 @@ #[cfg(target(o::o))] //~^ ERROR malformed `cfg` attribute input +//~| NOTE expected a valid identifier here +//~| NOTE for more information, visit +//~| ERROR malformed `cfg` attribute input +//~| NOTE expected this to be of the form `... = "..."` +//~| NOTE for more information, visit fn one() {} #[cfg(target(os = 8))] //~^ ERROR malformed `cfg` attribute input +//~| NOTE expected a string literal here +//~| NOTE for more information, visit fn two() {} #[cfg(target(os = "linux", pointer(width = "64")))] //~^ ERROR malformed `cfg` attribute input +//~| NOTE expected this to be of the form `... = "..."` +//~| NOTE for more information, visit fn three() {} #[cfg(target(true))] //~^ ERROR malformed `cfg` attribute input +//~| NOTE expected this to be of the form `... = "..."` +//~| NOTE for more information, visit fn four() {} #[cfg(target(clippy::os = "linux"))] //~^ ERROR malformed `cfg` attribute input +//~| NOTE for more information, visit +//~| NOTE expected a valid identifier here fn five() {} fn main() {} diff --git a/tests/ui/cfg/cfg-target-compact-errors.stderr b/tests/ui/cfg/cfg-target-compact-errors.stderr index 6152b991015ef..a228e473e6032 100644 --- a/tests/ui/cfg/cfg-target-compact-errors.stderr +++ b/tests/ui/cfg/cfg-target-compact-errors.stderr @@ -1,3 +1,18 @@ +error[E0539]: malformed `cfg` attribute input + --> $DIR/cfg-target-compact-errors.rs:5:1 + | +LL | #[cfg(target(o::o))] + | ^^^^^^^^^^^^^----^^^ + | | + | expected a valid identifier here + | + = note: for more information, visit +help: must be of the form + | +LL - #[cfg(target(o::o))] +LL + #[cfg(predicate)] + | + error[E0539]: malformed `cfg` attribute input --> $DIR/cfg-target-compact-errors.rs:5:1 | @@ -14,7 +29,7 @@ LL + #[cfg(predicate)] | error[E0539]: malformed `cfg` attribute input - --> $DIR/cfg-target-compact-errors.rs:9:1 + --> $DIR/cfg-target-compact-errors.rs:14:1 | LL | #[cfg(target(os = 8))] | ^^^^^^^^^^^^^^^^^^-^^^ @@ -29,7 +44,7 @@ LL + #[cfg(predicate)] | error[E0539]: malformed `cfg` attribute input - --> $DIR/cfg-target-compact-errors.rs:13:1 + --> $DIR/cfg-target-compact-errors.rs:20:1 | LL | #[cfg(target(os = "linux", pointer(width = "64")))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------------^^^ @@ -44,7 +59,7 @@ LL + #[cfg(predicate)] | error[E0539]: malformed `cfg` attribute input - --> $DIR/cfg-target-compact-errors.rs:17:1 + --> $DIR/cfg-target-compact-errors.rs:26:1 | LL | #[cfg(target(true))] | ^^^^^^^^^^^^^----^^^ @@ -59,7 +74,7 @@ LL + #[cfg(predicate)] | error[E0539]: malformed `cfg` attribute input - --> $DIR/cfg-target-compact-errors.rs:21:1 + --> $DIR/cfg-target-compact-errors.rs:32:1 | LL | #[cfg(target(clippy::os = "linux"))] | ^^^^^^^^^^^^^----------^^^^^^^^^^^^^ @@ -73,6 +88,6 @@ LL - #[cfg(target(clippy::os = "linux"))] LL + #[cfg(predicate)] | -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-invalid-format.stderr b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-invalid-format.stderr index 86a53a030f50c..01d081cc3041c 100644 --- a/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-invalid-format.stderr +++ b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-invalid-format.stderr @@ -2,9 +2,9 @@ error[E0539]: malformed `link` attribute input --> $DIR/import-name-type-invalid-format.rs:9:1 | LL | #[link(name = "foo", kind = "raw-dylib", import_name_type = 6)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--------------------^^ - | | - | expected this to be of the form `import_name_type = "..."` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ + | | + | expected a string literal here | = note: for more information, visit diff --git a/tests/ui/target-feature/invalid-attribute.rs b/tests/ui/target-feature/invalid-attribute.rs index 1022929118bd6..0c380d69b1433 100644 --- a/tests/ui/target-feature/invalid-attribute.rs +++ b/tests/ui/target-feature/invalid-attribute.rs @@ -31,7 +31,7 @@ extern "Rust" {} //~| NOTE expected this to be of the form `enable = "..."` #[target_feature(disable = "baz")] //~^ ERROR malformed `target_feature` attribute -//~| NOTE expected this to be of the form `enable = "..."` +//~| NOTE the only valid argument here is `enable` unsafe fn foo() {} #[target_feature(enable = "sse2")] diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index 64d4ceff5a87b..6de12edb037fe 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -56,7 +56,7 @@ error[E0539]: malformed `target_feature` attribute input LL | #[target_feature(disable = "baz")] | ^^^^^^^^^^^^^^^^^-------^^^^^^^^^^ | | - | expected this to be of the form `enable = "..."` + | the only valid argument here is `enable` | help: must be of the form | diff --git a/tests/ui/test-attrs/test-should-panic-attr.rs b/tests/ui/test-attrs/test-should-panic-attr.rs index b095099daad0f..8207c4c0be753 100644 --- a/tests/ui/test-attrs/test-should-panic-attr.rs +++ b/tests/ui/test-attrs/test-should-panic-attr.rs @@ -19,7 +19,7 @@ fn test2() { #[test] #[should_panic(expect)] //~^ ERROR malformed `should_panic` attribute input -//~| NOTE the only valid argument here is "expected" +//~| NOTE expected this to be of the form `expected = "..."` //~| NOTE for more information, visit fn test3() { panic!(); diff --git a/tests/ui/test-attrs/test-should-panic-attr.stderr b/tests/ui/test-attrs/test-should-panic-attr.stderr index 48f6b0d37cb10..327444d9885c8 100644 --- a/tests/ui/test-attrs/test-should-panic-attr.stderr +++ b/tests/ui/test-attrs/test-should-panic-attr.stderr @@ -22,9 +22,9 @@ error[E0539]: malformed `should_panic` attribute input --> $DIR/test-should-panic-attr.rs:20:1 | LL | #[should_panic(expect)] - | ^^^^^^^^^^^^^^--------^ - | | - | the only valid argument here is "expected" + | ^^^^^^^^^^^^^^^------^^ + | | + | expected this to be of the form `expected = "..."` | = note: for more information, visit help: try changing it to one of the following valid forms of the attribute