diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 73b0dbd1ceeba..0d2d1f5ea0dff 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -77,6 +77,7 @@ pub mod sso; pub mod stable_hash; pub mod stack; pub mod steal; +mod string_enum; pub mod svh; pub mod sync; pub mod tagged_ptr; diff --git a/compiler/rustc_data_structures/src/string_enum.rs b/compiler/rustc_data_structures/src/string_enum.rs new file mode 100644 index 0000000000000..d90bdb5006f69 --- /dev/null +++ b/compiler/rustc_data_structures/src/string_enum.rs @@ -0,0 +1,145 @@ +/// Like an enum, but the variants are tied to a string representation. +/// +/// Each variant is declared in one of three forms: +/// * `Variant => "primary"` — single canonical CLI string. +/// * `Variant => "primary" | "alias1" | "alias2"` — canonical string plus +/// one or more aliases that also parse to this variant. `to_str` and +/// `Display` return the canonical form. +/// * `Variant` (no `=>`) — the variant exists in the enum but has no CLI +/// string representation. Reachable only by code that produces the value +/// directly (e.g. a parser handling no-value or boolean fallthrough). +/// Calling `to_str` or `Display` on such a variant panics, and `FromStr` +/// will not produce it. +/// +/// Any variant may also carry an explicit discriminant +/// (`Variant = N` or `Variant = N => "primary"`), forwarded verbatim to +/// the generated enum. Use this when the discriminant values are +/// load-bearing (e.g. encoded on the wire or stable-hashed). +/// +/// Variants with a CLI string may also be marked `@no_from_str` +/// (`Variant => "primary" @no_from_str`), which makes the string +/// available to `to_str`/`Display` but excludes it from `FromStr`. +/// Use this for variants that have a textual identity for display +/// purposes but should not be constructible from untrusted user input +/// (e.g. variants that require out-of-band context to build correctly). +/// Such variants still appear in `STR_VARIANTS`/`ALL_STR_VARIANTS`; +/// callers that want only the strings the user is allowed to supply +/// should use `FROM_STR_VARIANTS` instead. +/// +/// Generates: +/// * `VARIANTS` — every variant, in declaration order. +/// * `STR_VARIANTS` — canonical string of each variant that has one, in +/// declaration order. +/// * `ALL_STR_VARIANTS` — every accepted string (canonical + aliases) in +/// declaration order. Use this when help text should list all accepted +/// forms. +/// * `FROM_STR_VARIANTS` — canonical string of each variant whose canonical +/// string is accepted by `FromStr` (i.e. `STR_VARIANTS` minus any variant +/// marked `@no_from_str`). Use this in diagnostics that list the inputs +/// the user is actually allowed to supply. +/// * `to_str()`, `Display`, `FromStr`. `FromStr::Err` is `()` because +/// diagnostic emission is handled by the caller. +#[macro_export] +macro_rules! string_enum { + ( + $(#[$meta:meta])* + $vis:vis enum $name:ident { + $( + $(#[$variant_meta:meta])* + $variant:ident $( = $disc:expr )? + $( => $repr:literal $( | $alias:literal )* + $( @ $no_from_str:ident )? )? , + )* + } + ) => { + $(#[$meta])* + $vis enum $name { + $( + $(#[$variant_meta])* + $variant $( = $disc )?, + )* + } + + impl $name { + #[allow(dead_code)] + $vis const VARIANTS: &'static [Self] = &[ + $( Self::$variant, )* + ]; + #[allow(dead_code)] + $vis const STR_VARIANTS: &'static [&'static str] = &[ + $( $( $repr, )? )* + ]; + #[allow(dead_code)] + $vis const ALL_STR_VARIANTS: &'static [&'static str] = &[ + $( $( $repr, $( $alias, )* )? )* + ]; + #[allow(dead_code)] + $vis const FROM_STR_VARIANTS: &'static [&'static str] = + $crate::__string_enum_from_str_arr!( + @collect [] + $( $( $repr $( @ $no_from_str )? , )? )* + ); + + #[allow(unreachable_patterns)] + $vis const fn to_str(&self) -> &'static str { + match self { + $( $( Self::$variant => $repr, )? )* + _ => panic!("variant has no CLI string representation"), + } + } + } + + impl ::std::fmt::Display for $name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::std::fmt::Display::fmt(self.to_str(), f) + } + } + + impl ::std::str::FromStr for $name { + type Err = (); + + #[allow(unreachable_code)] + fn from_str(s: &str) -> Result { + match s { + $( $( $repr $( | $alias )* => { + $( + $crate::__string_enum_check_no_from_str!($no_from_str); + return Err(()); + )? + Ok(Self::$variant) + }, )? )* + _ => Err(()), + } + } + } + } +} + +/// Validates that a `string_enum!` variant's `@`-marker is spelled exactly +/// `no_from_str`. Used internally by [`string_enum!`]; not part of the public +/// surface. +#[doc(hidden)] +#[macro_export] +macro_rules! __string_enum_check_no_from_str { + (no_from_str) => {}; +} + +/// Builds a `&[&str]` literal containing only the canonical strings of +/// variants accepted by `FromStr`. Token-tree munches a comma-terminated +/// stream of `$repr` (or `$repr @ no_from_str`) entries into an accumulator, +/// then emits the full slice literal in one go — needed because in +/// expression position a macro must expand to a single expression. Used +/// internally by [`string_enum!`]; not part of the public surface. +#[doc(hidden)] +#[macro_export] +macro_rules! __string_enum_from_str_arr { + (@collect [$($acc:literal,)*]) => { + &[ $($acc,)* ] + }; + (@collect [$($acc:literal,)*] $repr:literal @ no_from_str , $($rest:tt)*) => { + $crate::__string_enum_from_str_arr!(@collect [$($acc,)*] $($rest)*) + }; + (@collect [$($acc:literal,)*] $repr:literal , $($rest:tt)*) => { + $crate::__string_enum_from_str_arr!(@collect [$($acc,)* $repr,] $($rest)*) + }; +} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index a3cb2fa80f63b..7f7286607fa2f 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -741,7 +741,7 @@ fn print_crate_info( continue; } let level = builder.lint_level_spec(lint).level(); - println_info!("{}={}", lint.name_lower(), level.as_str()); + println_info!("{}={}", lint.name_lower(), level.to_str()); } } Cfg => { @@ -1027,7 +1027,7 @@ Available lint options: safe_println!( " {} {:7.7} {}", padded(&name), - lint.default_level(sess.edition()).as_str(), + lint.default_level(sess.edition()).to_str(), lint.desc ); } diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 04ac140f33261..a994c0efb9db5 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -160,7 +160,7 @@ impl Emitter for JsonEmitter { } fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) { - let lint_level = lint_level.as_str(); + let lint_level = lint_level.to_str(); let data = UnusedExterns { lint_level, unused_extern_names: unused_externs }; let result = self.emit(EmitTyped::UnusedExtern(data)); if let Err(e) = result { diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 9cf2be337fccb..5aa6817ab77bc 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -681,24 +681,28 @@ impl rustc_serialize::Encodable for DocAttribute } } -/// How to perform collapse macros debug info -/// if-ext - if macro from different crate (related to callsite code) -/// | cmd \ attr | no | (unspecified) | external | yes | -/// | no | no | no | no | no | -/// | (unspecified) | no | no | if-ext | yes | -/// | external | no | if-ext | if-ext | yes | -/// | yes | yes | yes | yes | yes | -#[derive(Copy, Clone, Debug, Hash, PartialEq)] -#[derive(StableHash, Encodable, Decodable, PrintAttribute)] -pub enum CollapseMacroDebuginfo { - /// Don't collapse debuginfo for the macro - No = 0, - /// Unspecified value - Unspecified = 1, - /// Collapse debuginfo if the macro comes from a different crate - External = 2, - /// Collapse debuginfo for the macro - Yes = 3, +rustc_data_structures::string_enum! { + /// How to perform collapse macros debug info + /// if-ext - if macro from different crate (related to callsite code) + /// | cmd \ attr | no | (unspecified) | external | yes | + /// | no | no | no | no | no | + /// | (unspecified) | no | no | if-ext | yes | + /// | external | no | if-ext | if-ext | yes | + /// | yes | yes | yes | yes | yes | + #[derive(Copy, Clone, Debug, Hash, PartialEq)] + #[derive(StableHash, Encodable, Decodable, PrintAttribute)] + pub enum CollapseMacroDebuginfo { + /// Don't collapse debuginfo for the macro. Reachable only via boolean + /// false (`no`, `off`, `false`, etc.). + No = 0, + /// Unspecified value. Reachable only as the default when the flag is + /// not passed; never CLI-settable. + Unspecified = 1, + /// Collapse debuginfo if the macro comes from a different crate + External = 2 => "external", + /// Collapse debuginfo for the macro. Reachable only via boolean true. + Yes = 3, + } } /// Crate type, as specified by `#![crate_type]` diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index ef23085417352..9a78103bb190f 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -619,7 +619,7 @@ where self.sess.dcx().emit_err(OverruledAttribute { span: src.span(), overruled: src.span(), - lint_level: level.as_str(), + lint_level: level.to_str(), lint_source: src.name(), sub, }); @@ -629,7 +629,7 @@ where src.span().into(), OverruledAttributeLint { overruled: src.span(), - lint_level: level.as_str(), + lint_level: level.to_str(), lint_source: src.name(), sub, }, @@ -928,7 +928,7 @@ where UNUSED_ATTRIBUTES, lint_attr_span.into(), IgnoredUnlessCrateSpecified { - level: level_spec.level().as_str(), + level: level_spec.level().to_str(), name: lint_attr_name, }, ); diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 6949090781cf6..528c8f2265917 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -146,70 +146,48 @@ impl From for LintExpectationId { } } -/// Setting for how to handle a lint. -/// -/// See: -#[derive( - Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable, StableHash -)] -pub enum Level { - /// The `allow` level will not issue any message. - Allow, - /// The `expect` level will suppress the lint message but in turn produce a message - /// if the lint wasn't issued in the expected scope. `Expect` should not be used as - /// an initial level for a lint. - /// - /// Note that this still means that the lint is enabled in this position and should - /// be emitted, this will in turn fulfill the expectation and suppress the lint. - /// - /// See RFC 2383. +rustc_data_structures::string_enum! { + /// Setting for how to handle a lint. /// - /// Requires a [`LintExpectationId`] to later link a lint emission to the actual - /// expectation. It can be ignored in most cases. - Expect, - /// The `warn` level will produce a warning if the lint was violated, however the - /// compiler will continue with its execution. - Warn, - /// This lint level is a special case of [`Warn`], that can't be overridden. This is used - /// to ensure that a lint can't be suppressed. This lint level can currently only be set - /// via the console and is therefore session specific. - /// - /// Requires a [`LintExpectationId`] to fulfill expectations marked via the - /// `#[expect]` attribute, that will still be suppressed due to the level. - ForceWarn, - /// The `deny` level will produce an error and stop further execution after the lint - /// pass is complete. - Deny, - /// `Forbid` is equivalent to the `deny` level but can't be overwritten like the previous - /// levels. - Forbid, + /// See: + #[derive( + Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable, StableHash + )] + pub enum Level { + /// The `allow` level will not issue any message. + Allow => "allow", + /// The `expect` level will suppress the lint message but in turn produce a message + /// if the lint wasn't issued in the expected scope. `Expect` should not be used as + /// an initial level for a lint. + /// + /// Note that this still means that the lint is enabled in this position and should + /// be emitted, this will in turn fulfill the expectation and suppress the lint. + /// + /// See RFC 2383. + /// + /// Requires a [`LintExpectationId`] to later link a lint emission to the actual + /// expectation. It can be ignored in most cases. + Expect => "expect" @no_from_str, + /// The `warn` level will produce a warning if the lint was violated, however the + /// compiler will continue with its execution. + Warn => "warn", + /// This lint level is a special case of [`Warn`], that can't be overridden. This is used + /// to ensure that a lint can't be suppressed. This lint level can currently only be set + /// via the console and is therefore session specific. + /// + /// Requires a [`LintExpectationId`] to fulfill expectations marked via the + /// `#[expect]` attribute, that will still be suppressed due to the level. + ForceWarn => "force-warn" @no_from_str, + /// The `deny` level will produce an error and stop further execution after the lint + /// pass is complete. + Deny => "deny", + /// `Forbid` is equivalent to the `deny` level but can't be overwritten like the previous + /// levels. + Forbid => "forbid", + } } impl Level { - /// Converts a level to a lower-case string. - pub fn as_str(self) -> &'static str { - match self { - Level::Allow => "allow", - Level::Expect => "expect", - Level::Warn => "warn", - Level::ForceWarn => "force-warn", - Level::Deny => "deny", - Level::Forbid => "forbid", - } - } - - /// Converts a lower-case string to a level. This will never construct the expect - /// level as that would require a [`LintExpectationId`]. - pub fn from_str(x: &str) -> Option { - match x { - "allow" => Some(Level::Allow), - "warn" => Some(Level::Warn), - "deny" => Some(Level::Deny), - "forbid" => Some(Level::Forbid), - "expect" | _ => None, - } - } - /// Converts an `Option` to a level. pub fn from_opt_symbol(s: Option) -> Option { s.and_then(Self::from_symbol) diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index b852a107d3343..f182428e48af1 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -312,7 +312,7 @@ fn explain_lint_level_source( } match src { LintLevelSource::Default => { - let level_str = level.as_str(); + let level_str = level.to_str(); match lint_group_name(lint) { Some(group_name) => { err.note_once(format!("`#[{level_str}({name})]` (part of `#[{level_str}({group_name})]`) on by default")); @@ -354,7 +354,7 @@ fn explain_lint_level_source( } err.span_note_once(span, "the lint level is defined here"); if lint_attr_name.as_str() != name { - let level_str = level.as_str(); + let level_str = level.to_str(); err.note_once(format!( "`#[{level_str}({name})]` implied by `#[{level_str}({lint_attr_name})]`" )); diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index d22738ec0b980..4cb3c3bf4a784 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -99,7 +99,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>( span: arm.pat.data().span, lint_span: level_spec.src.span(), suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()), - lint_level: level.as_str(), + lint_level: level.to_str(), lint_name: "non_exhaustive_omitted_patterns", }); } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 37488ebbf1e8f..50150feac1912 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -16,7 +16,7 @@ use externs::{ExternOpt, split_extern_opt}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hash::{StableHasher, StableOrd}; use rustc_errors::emitter::HumanReadableErrorType; -use rustc_errors::{ColorConfig, DiagCtxtFlags}; +use rustc_errors::{ColorConfig, Diag, DiagCtxtFlags, FatalAbort}; use rustc_feature::UnstableFeatures; use rustc_hashes::Hash64; use rustc_macros::{BlobDecodable, Decodable, Encodable, StableHash}; @@ -49,46 +49,53 @@ pub mod sigpipe; /// Special CPU name requesting the CPU of the current host. pub const NATIVE_CPU: &str = "native"; -/// The different settings that the `-C strip` flag can have. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum Strip { - /// Do not strip at all. - None, +rustc_data_structures::string_enum! { + /// The different settings that the `-C strip` flag can have. + #[derive(Clone, Copy, PartialEq, Hash, Debug)] + pub enum Strip { + /// Do not strip at all. + None => "none", - /// Strip debuginfo. - Debuginfo, + /// Strip debuginfo. + Debuginfo => "debuginfo", - /// Strip all symbols. - Symbols, + /// Strip all symbols. + Symbols => "symbols", + } } -/// The different settings that the `-C control-flow-guard` flag can have. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum CFGuard { - /// Do not emit Control Flow Guard metadata or checks. - Disabled, +rustc_data_structures::string_enum! { + /// The different settings that the `-C control-flow-guard` flag can have. + #[derive(Clone, Copy, PartialEq, Hash, Debug)] + pub enum CFGuard { + /// Do not emit Control Flow Guard metadata or checks. Reachable only + /// via boolean false (`no`, `off`, `false`, etc.). + Disabled, - /// Emit Control Flow Guard metadata but no checks. - NoChecks, + /// Emit Control Flow Guard metadata but no checks. + NoChecks => "nochecks", - /// Emit Control Flow Guard metadata and checks. - Checks, + /// Emit Control Flow Guard metadata and checks. + Checks => "checks", + } } -/// The different settings that the `-Z cf-protection` flag can have. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum CFProtection { - /// Do not enable control-flow protection - None, +rustc_data_structures::string_enum! { + /// The different settings that the `-Z cf-protection` flag can have. + #[derive(Clone, Copy, PartialEq, Hash, Debug)] + pub enum CFProtection { + /// Do not enable control-flow protection + None => "none", - /// Emit control-flow protection for branches (enables indirect branch tracking). - Branch, + /// Emit control-flow protection for branches (enables indirect branch tracking). + Branch => "branch", - /// Emit control-flow protection for returns. - Return, + /// Emit control-flow protection for returns. + Return => "return", - /// Emit control-flow protection for both branches and returns. - Full, + /// Emit control-flow protection for both branches and returns. + Full => "full", + } } #[derive(Clone, Copy, Debug, PartialEq, Hash, StableHash, Encodable, Decodable)] @@ -127,30 +134,36 @@ pub enum Lto { Fat, } -/// The different settings that the `-C lto` flag can have. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum LtoCli { - /// `-C lto=no` - No, - /// `-C lto=yes` - Yes, - /// `-C lto` - NoParam, - /// `-C lto=thin` - Thin, - /// `-C lto=fat` - Fat, - /// No `-C lto` flag passed - Unspecified, +rustc_data_structures::string_enum! { + /// The different settings that the `-C lto` flag can have. + #[derive(Clone, Copy, PartialEq, Hash, Debug)] + pub enum LtoCli { + /// `-C lto=no`. Reachable only via boolean false. + No, + /// `-C lto=yes`. Reachable only via boolean true. + Yes, + /// `-C lto`. Reachable only when the flag is passed with no value. + NoParam, + /// `-C lto=thin` + Thin => "thin", + /// `-C lto=fat` + Fat => "fat", + /// No `-C lto` flag passed. + Unspecified, + } } -/// The different settings that the `-C instrument-coverage` flag can have. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum InstrumentCoverage { - /// `-C instrument-coverage=no` (or `off`, `false` etc.) - No, - /// `-C instrument-coverage` or `-C instrument-coverage=yes` - Yes, +rustc_data_structures::string_enum! { + /// The different settings that the `-C instrument-coverage` flag can have. + #[derive(Clone, Copy, PartialEq, Hash, Debug)] + pub enum InstrumentCoverage { + /// `-C instrument-coverage=no` (or `off`, `false`, `0`, etc.). `"0"` + /// is a historical alias retained for backwards compatibility. + No => "0", + /// `-C instrument-coverage` or `-C instrument-coverage=yes`. `"all"` + /// is a historical alias retained for backwards compatibility. + Yes => "all", + } } /// Individual flag values controlled by `-Zcoverage-options`. @@ -534,15 +547,17 @@ impl LocationDetail { } } -/// Values for the `-Z fmt-debug` flag. -#[derive(Copy, Clone, PartialEq, Hash, Debug)] -pub enum FmtDebug { - /// Derive fully-featured implementation - Full, - /// Print only type name, without fields - Shallow, - /// `#[derive(Debug)]` and `{:?}` are no-ops - None, +rustc_data_structures::string_enum! { + /// Values for the `-Z fmt-debug` flag. + #[derive(Copy, Clone, PartialEq, Hash, Debug)] + pub enum FmtDebug { + /// Derive fully-featured implementation + Full => "full", + /// Print only type name, without fields + Shallow => "shallow", + /// `#[derive(Debug)]` and `{:?}` are no-ops + None => "none", + } } impl FmtDebug { @@ -574,27 +589,33 @@ pub enum SymbolManglingVersion { Hashed, } -#[derive(Clone, Copy, Debug, PartialEq, Hash)] -pub enum DebugInfo { - None, - LineDirectivesOnly, - LineTablesOnly, - Limited, - Full, +rustc_data_structures::string_enum! { + #[derive(Clone, Copy, Debug, PartialEq, Hash)] + pub enum DebugInfo { + None => "none" | "0", + LineDirectivesOnly => "line-directives-only", + LineTablesOnly => "line-tables-only", + Limited => "limited" | "1", + Full => "full" | "2", + } } -#[derive(Clone, Copy, Debug, PartialEq, Hash)] -pub enum DebugInfoCompression { - None, - Zlib, - Zstd, +rustc_data_structures::string_enum! { + #[derive(Clone, Copy, Debug, PartialEq, Hash)] + pub enum DebugInfoCompression { + None => "none", + Zlib => "zlib", + Zstd => "zstd", + } } -#[derive(Clone, Copy, Debug, PartialEq, Hash)] -pub enum MirStripDebugInfo { - None, - LocalsInTinyFunctions, - AllLocals, +rustc_data_structures::string_enum! { + #[derive(Clone, Copy, Debug, PartialEq, Hash)] + pub enum MirStripDebugInfo { + None => "none", + LocalsInTinyFunctions => "locals-in-tiny-functions", + AllLocals => "all-locals", + } } /// Split debug-information is enabled by `-C split-debuginfo`, this enum is only used if split @@ -1979,7 +2000,7 @@ pub fn get_cmd_lint_options( let mut describe_lints = false; for level in [lint::Allow, lint::Warn, lint::ForceWarn, lint::Deny, lint::Forbid] { - for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) { + for (arg_pos, lint_name) in matches.opt_strs_pos(level.to_str()) { if lint_name == "help" { describe_lints = true; } else { @@ -1996,13 +2017,56 @@ pub fn get_cmd_lint_options( .collect(); let lint_cap = matches.opt_str("cap-lints").map(|cap| { - lint::Level::from_str(&cap) - .unwrap_or_else(|| early_dcx.early_fatal(format!("unknown lint level: `{cap}`"))) + cap.parse::() + .unwrap_or_else(|()| early_dcx.early_fatal(format!("unknown lint level: `{cap}`"))) }); (lint_opts, describe_lints, lint_cap) } +/// Build a fatal "unknown {label}: ..." diagnostic listing the valid values. +/// +/// Use this whenever a command-line argument expects one of a fixed set of +/// string values (typically the `STR_VARIANTS` of an enum declared via +/// [`rustc_data_structures::string_enum!`]) and the user supplied something +/// else. The returned [`Diag`] is left un-emitted so the caller can attach +/// further help notes; call `.emit()` to abort. +/// +/// `label` is the singular noun phrase for the value (e.g. `"print request"`, +/// `"lint level"`); the help line pluralises it by appending `"s"`. +pub fn build_unknown_arg_value_diag<'a>( + early_dcx: &'a EarlyDiagCtxt, + label: &str, + bad_value: &str, + valid_values: &[&str], +) -> Diag<'a, FatalAbort> { + let valid = valid_values.iter().map(|v| format!("`{v}`")).collect::>().join(", "); + let mut diag = early_dcx.early_struct_fatal(format!("unknown {label}: `{bad_value}`")); + diag.help(format!("valid {label}s are: {valid}")); + diag +} + +/// Build a fatal "incorrect value for option" diagnostic listing the valid +/// values. Used by the `-C`/`-Z` option dispatcher when a parser rejects its +/// input and the option declared a fixed value vocabulary via `[VALUES: ...]`. +/// +/// `outputname` is `"codegen"` or `"unstable"`; `key` is the option name +/// (e.g. `"strip"`). +pub fn build_unknown_option_value_diag<'a>( + early_dcx: &'a EarlyDiagCtxt, + outputname: &str, + key: &str, + bad_value: &str, + valid_values: &[&str], +) -> Diag<'a, FatalAbort> { + let valid = valid_values.iter().map(|v| format!("`{v}`")).collect::>().join(", "); + let mut diag = early_dcx.early_struct_fatal(format!( + "incorrect value `{bad_value}` for {outputname} option `{key}`" + )); + diag.help(format!("valid values are: {valid}")); + diag +} + /// Parses the `--color` flag. pub fn parse_color(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> ColorConfig { match matches.opt_str("color").as_deref() { @@ -3424,19 +3488,22 @@ impl PatchableFunctionEntry { } } -/// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy, -/// or future prototype. -#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] -pub enum Polonius { - /// The default value: disabled. - #[default] - Off, +rustc_data_structures::string_enum! { + /// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy, + /// or future prototype. + #[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] + pub enum Polonius { + /// The default value: disabled. Reachable only as the default when + /// `-Zpolonius` is not passed. + #[default] + Off, - /// Legacy version, using datalog and the `polonius-engine` crate. Historical value for `-Zpolonius`. - Legacy, + /// Legacy version, using datalog and the `polonius-engine` crate. Historical value for `-Zpolonius`. + Legacy => "legacy", - /// In-tree prototype, extending the NLL infrastructure. - Next, + /// In-tree prototype, extending the NLL infrastructure. + Next => "next", + } } impl Polonius { @@ -3464,15 +3531,17 @@ impl Default for InliningThreshold { } } -/// The different settings that the `-Zfunction-return` flag can have. -#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] -pub enum FunctionReturn { - /// Keep the function return unmodified. - #[default] - Keep, +rustc_data_structures::string_enum! { + /// The different settings that the `-Zfunction-return` flag can have. + #[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] + pub enum FunctionReturn { + /// Keep the function return unmodified. + #[default] + Keep => "keep", - /// Replace returns with jumps to thunk, without emitting the thunk. - ThunkExtern, + /// Replace returns with jumps to thunk, without emitting the thunk. + ThunkExtern => "thunk-extern", + } } /// Whether extra span comments are included when dumping MIR, via the `-Z mir-include-spans` flag. diff --git a/compiler/rustc_session/src/config/print_request.rs b/compiler/rustc_session/src/config/print_request.rs index 65e25ab5e6006..e2af96bcf9da6 100644 --- a/compiler/rustc_session/src/config/print_request.rs +++ b/compiler/rustc_session/src/config/print_request.rs @@ -1,15 +1,16 @@ //! Code for dealing with `--print` requests. -use std::fmt; +use std::str::FromStr; use std::sync::LazyLock; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::string_enum; use crate::EarlyDiagCtxt; use crate::config::{ - CodegenOptions, OutFileName, UnstableOptions, nightly_options, split_out_file_name, + CodegenOptions, OutFileName, UnstableOptions, build_unknown_arg_value_diag, nightly_options, + split_out_file_name, }; -use crate::macros::AllVariants; #[derive(Clone, PartialEq, Debug)] pub struct PrintRequest { @@ -18,74 +19,41 @@ pub struct PrintRequest { pub arg: Option, } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -#[derive(AllVariants)] -pub enum PrintKind { - // tidy-alphabetical-start - AllTargetSpecsJson, - BackendHasMnemonic, - BackendHasZstd, - CallingConventions, - Cfg, - CheckCfg, - CodeModels, - CrateName, - CrateRootLintLevels, - DeploymentTarget, - FileNames, - HostTuple, - LinkArgs, - NativeStaticLibs, - RelocationModels, - SplitDebuginfo, - StackProtectorStrategies, - SupportedCrateTypes, - Sysroot, - TargetCPUs, - TargetFeatures, - TargetLibdir, - TargetList, - TargetSpecJson, - TargetSpecJsonSchema, - TlsModels, - // tidy-alphabetical-end +string_enum! { + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + pub enum PrintKind { + // tidy-alphabetical-start + AllTargetSpecsJson => "all-target-specs-json", + BackendHasMnemonic => "backend-has-mnemonic", + BackendHasZstd => "backend-has-zstd", + CallingConventions => "calling-conventions", + Cfg => "cfg", + CheckCfg => "check-cfg", + CodeModels => "code-models", + CrateName => "crate-name", + CrateRootLintLevels => "crate-root-lint-levels", + DeploymentTarget => "deployment-target", + FileNames => "file-names", + HostTuple => "host-tuple", + LinkArgs => "link-args", + NativeStaticLibs => "native-static-libs", + RelocationModels => "relocation-models", + SplitDebuginfo => "split-debuginfo", + StackProtectorStrategies => "stack-protector-strategies", + SupportedCrateTypes => "supported-crate-types", + Sysroot => "sysroot", + TargetCPUs => "target-cpus", + TargetFeatures => "target-features", + TargetLibdir => "target-libdir", + TargetList => "target-list", + TargetSpecJson => "target-spec-json", + TargetSpecJsonSchema => "target-spec-json-schema", + TlsModels => "tls-models", + // tidy-alphabetical-end + } } impl PrintKind { - fn name(self) -> &'static str { - use PrintKind::*; - match self { - // tidy-alphabetical-start - AllTargetSpecsJson => "all-target-specs-json", - BackendHasMnemonic => "backend-has-mnemonic", - BackendHasZstd => "backend-has-zstd", - CallingConventions => "calling-conventions", - Cfg => "cfg", - CheckCfg => "check-cfg", - CodeModels => "code-models", - CrateName => "crate-name", - CrateRootLintLevels => "crate-root-lint-levels", - DeploymentTarget => "deployment-target", - FileNames => "file-names", - HostTuple => "host-tuple", - LinkArgs => "link-args", - NativeStaticLibs => "native-static-libs", - RelocationModels => "relocation-models", - SplitDebuginfo => "split-debuginfo", - StackProtectorStrategies => "stack-protector-strategies", - SupportedCrateTypes => "supported-crate-types", - Sysroot => "sysroot", - TargetCPUs => "target-cpus", - TargetFeatures => "target-features", - TargetLibdir => "target-libdir", - TargetList => "target-list", - TargetSpecJson => "target-spec-json", - TargetSpecJsonSchema => "target-spec-json-schema", - TlsModels => "tls-models", - // tidy-alphabetical-end - } - } - fn is_stable(self) -> bool { use PrintKind::*; match self { @@ -120,21 +88,11 @@ impl PrintKind { TargetSpecJsonSchema => false, } } - - fn from_str(s: &str) -> Option { - Self::ALL_VARIANTS.iter().find(|kind| kind.name() == s).copied() - } -} - -impl fmt::Display for PrintKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.name().fmt(f) - } } pub(crate) static PRINT_HELP: LazyLock = LazyLock::new(|| { - let print_kinds = - PrintKind::ALL_VARIANTS.iter().map(|kind| kind.name()).collect::>().join("|"); + let print_kinds: String = + PrintKind::VARIANTS.iter().map(PrintKind::to_str).intersperse("|").collect(); format!( "Compiler information to print on stdout (or to a file)\n\ INFO may be one of <{print_kinds}>.", @@ -187,7 +145,7 @@ pub(crate) fn collect_print_requests( for example: `--print=backend-has-mnemonic:RET`", ); } - } else if let Some(print_kind) = PrintKind::from_str(req) { + } else if let Ok(print_kind) = PrintKind::from_str(req) { check_print_request_stability(early_dcx, unstable_opts, print_kind); (print_kind, None) } else { @@ -224,21 +182,21 @@ fn check_print_request_stability( } fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! { - let prints = PrintKind::ALL_VARIANTS + let valid: Vec<&str> = PrintKind::VARIANTS .iter() - // If we're not on nightly, we don't want to print unstable options .filter(|kind| is_nightly || kind.is_stable()) - .map(|kind| format!("`{kind}`")) - .collect::>() - .join(", "); + .map(|kind| kind.to_str()) + .collect(); - let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`")); - diag.help(format!("valid print requests are: {prints}")); + let mut diag = build_unknown_arg_value_diag(early_dcx, "print request", req, &valid); if req == "lints" { - diag.help(format!("use `-Whelp` to print a list of lints")); + diag.help("use `-Whelp` to print a list of lints"); } - diag.help(format!("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information")); + diag.help( + "for more information, see the rustc book: \ + https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information", + ); diag.emit() } diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 94566c943d7ed..65b6470660f51 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -4,7 +4,6 @@ #![feature(const_trait_impl)] #![feature(default_field_values)] #![feature(iter_intersperse)] -#![feature(macro_derive)] #![feature(macro_metavar_expr)] #![feature(option_into_flat_iter)] #![feature(rustc_attrs)] @@ -26,7 +25,6 @@ pub mod utils; pub mod config; pub mod cstore; pub mod filesearch; -mod macros; mod options; pub mod output; pub mod search_paths; diff --git a/compiler/rustc_session/src/macros.rs b/compiler/rustc_session/src/macros.rs deleted file mode 100644 index 8f9a12d00cc7f..0000000000000 --- a/compiler/rustc_session/src/macros.rs +++ /dev/null @@ -1,27 +0,0 @@ -/// Derivable trait for enums with no fields (i.e. C-style enums) that want to -/// allow iteration over a list of all variant values. -pub(crate) trait AllVariants: Copy + 'static { - const ALL_VARIANTS: &[Self]; -} - -macro_rules! AllVariantsDerive { - derive() ( - $(#[$meta:meta])* - $vis:vis enum $Type:ident { - $( - $(#[$varmeta:meta])* - $Variant:ident $( = $value:literal )? - ), *$(,)? - } - ) => { - impl $crate::macros::AllVariants for $Type { - const ALL_VARIANTS: &[$Type] = &[ - $( $Type::$Variant, )* - ]; - } - }; -} - -// For some reason the compiler won't allow `pub(crate) use AllVariants` due -// to a conflict with the trait of the same name, but will allow this form. -pub(crate) use AllVariantsDerive as AllVariants; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 5b71c0435185a..361f92afeb12b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -504,6 +504,7 @@ macro_rules! options { $init:expr, $parse:ident, [$dep_tracking_marker:ident] + $( [ VALUES: $values:expr ] )? $( { TARGET_MODIFIER: $tmod_variant:ident } )? $( { MITIGATION: $mitigation_variant:ident } )? , @@ -634,6 +635,7 @@ macro_rules! options { name: stringify!($opt), setter: $optmod::$opt, type_desc: desc::$parse, + valid_values: None $( .or(Some($values)) )?, desc: $desc, removed: None $( .or(Some(RemovedOption::$removed)) )?, tmod: None $( .or(Some( @@ -691,6 +693,12 @@ pub struct OptionDesc { setter: OptionSetter, // description for return value/type from mod desc type_desc: &'static str, + // Fixed set of accepted string values, when the option's parser maps a + // closed vocabulary onto an enum (declared via `[VALUES: ...]` in the + // `options!` invocation). When set, the dispatcher emits a structured + // "incorrect value" diagnostic listing these values instead of the + // free-form `type_desc` description. + valid_values: Option<&'static [&'static str]>, // description for option from options table desc: &'static str, removed: Option, @@ -725,7 +733,16 @@ fn build_options( let option_to_lookup = key.replace('-', "_"); match descrs.iter().find(|opt_desc| opt_desc.name == option_to_lookup) { - Some(OptionDesc { name: _, setter, type_desc, desc, removed, tmod, mitigation }) => { + Some(OptionDesc { + name: _, + setter, + type_desc, + valid_values, + desc, + removed, + tmod, + mitigation, + }) => { if let Some(removed) = removed { // deprecation works for prefixed options only assert!(!prefix.is_empty()); @@ -745,11 +762,15 @@ fn build_options( "{outputname} option `{key}` requires {type_desc} (`-{prefix} {key}=`)" ), ), - Some(value) => early_dcx.early_fatal( - format!( + Some(value) => match valid_values { + Some(values) => build_unknown_option_value_diag( + early_dcx, outputname, &key, value, values, + ) + .emit(), + None => early_dcx.early_fatal(format!( "incorrect value `{value}` for {outputname} option `{key}` - {type_desc} was expected" - ), - ), + )), + }, } } if let Some(tmod) = *tmod { @@ -930,6 +951,59 @@ pub mod parse { true } + /// Parse a `string_enum!`-style value: write the parsed variant into `slot` + /// on success, or return false to let the dispatcher emit the structured + /// diagnostic from the option's `[VALUES: ...]` vocabulary. + pub(crate) fn parse_string_enum>(slot: &mut T, v: Option<&str>) -> bool { + match v.map(T::from_str) { + Some(Ok(value)) => { + *slot = value; + true + } + _ => false, + } + } + + /// Like [`parse_string_enum`], but also accepts boolean spellings + /// (`yes`/`no`/`y`/`n`/`on`/`off`/`true`/`false`) and the no-value case. + /// Each of `no_value`, `bool_true`, and `bool_false` is the variant the + /// caller wants that input shape to map to, or `None` to reject it. + pub(crate) fn parse_string_enum_with_bool>( + slot: &mut T, + v: Option<&str>, + no_value: Option, + bool_true: Option, + bool_false: Option, + ) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + let mapped = if bool_arg.unwrap() { bool_true } else { bool_false }; + if let Some(value) = mapped { + *slot = value; + return true; + } + } + } + match v { + None => { + if let Some(value) = no_value { + *slot = value; + true + } else { + false + } + } + Some(s) => match s.parse() { + Ok(value) => { + *slot = value; + true + } + Err(()) => false, + }, + } + } + /// This is for boolean options that don't take a value, and are true simply /// by existing on the command-line. /// @@ -980,17 +1054,7 @@ pub mod parse { /// Parses whether polonius is enabled, and if so, which version. pub(crate) fn parse_polonius(slot: &mut Polonius, v: Option<&str>) -> bool { - match v { - Some("legacy") | None => { - *slot = Polonius::Legacy; - true - } - Some("next") => { - *slot = Polonius::Next; - true - } - _ => false, - } + parse_string_enum_with_bool(slot, v, Some(Polonius::Legacy), None, None) } pub(crate) fn parse_annotate_moves(slot: &mut AnnotateMoves, v: Option<&str>) -> bool { @@ -1115,14 +1179,8 @@ pub mod parse { true } - pub(crate) fn parse_fmt_debug(opt: &mut FmtDebug, v: Option<&str>) -> bool { - *opt = match v { - Some("full") => FmtDebug::Full, - Some("shallow") => FmtDebug::Shallow, - Some("none") => FmtDebug::None, - _ => return false, - }; - true + pub(crate) fn parse_fmt_debug(slot: &mut FmtDebug, v: Option<&str>) -> bool { + parse_string_enum(slot, v) } pub(crate) fn parse_location_detail(ld: &mut LocationDetail, v: Option<&str>) -> bool { @@ -1366,85 +1424,42 @@ pub mod parse { } pub(crate) fn parse_strip(slot: &mut Strip, v: Option<&str>) -> bool { - match v { - Some("none") => *slot = Strip::None, - Some("debuginfo") => *slot = Strip::Debuginfo, - Some("symbols") => *slot = Strip::Symbols, - _ => return false, - } - true + parse_string_enum(slot, v) } pub(crate) fn parse_cfguard(slot: &mut CFGuard, v: Option<&str>) -> bool { - if v.is_some() { - let mut bool_arg = None; - if parse_opt_bool(&mut bool_arg, v) { - *slot = if bool_arg.unwrap() { CFGuard::Checks } else { CFGuard::Disabled }; - return true; - } - } - - *slot = match v { - None => CFGuard::Checks, - Some("checks") => CFGuard::Checks, - Some("nochecks") => CFGuard::NoChecks, - Some(_) => return false, - }; - true + parse_string_enum_with_bool( + slot, + v, + Some(CFGuard::Checks), + Some(CFGuard::Checks), + Some(CFGuard::Disabled), + ) } pub(crate) fn parse_cfprotection(slot: &mut CFProtection, v: Option<&str>) -> bool { - if v.is_some() { - let mut bool_arg = None; - if parse_opt_bool(&mut bool_arg, v) { - *slot = if bool_arg.unwrap() { CFProtection::Full } else { CFProtection::None }; - return true; - } - } - - *slot = match v { - None | Some("none") => CFProtection::None, - Some("branch") => CFProtection::Branch, - Some("return") => CFProtection::Return, - Some("full") => CFProtection::Full, - Some(_) => return false, - }; - true + parse_string_enum_with_bool( + slot, + v, + Some(CFProtection::None), + Some(CFProtection::Full), + Some(CFProtection::None), + ) } pub(crate) fn parse_debuginfo(slot: &mut DebugInfo, v: Option<&str>) -> bool { - match v { - Some("0") | Some("none") => *slot = DebugInfo::None, - Some("line-directives-only") => *slot = DebugInfo::LineDirectivesOnly, - Some("line-tables-only") => *slot = DebugInfo::LineTablesOnly, - Some("1") | Some("limited") => *slot = DebugInfo::Limited, - Some("2") | Some("full") => *slot = DebugInfo::Full, - _ => return false, - } - true + parse_string_enum(slot, v) } pub(crate) fn parse_debuginfo_compression( slot: &mut DebugInfoCompression, v: Option<&str>, ) -> bool { - match v { - Some("none") => *slot = DebugInfoCompression::None, - Some("zlib") => *slot = DebugInfoCompression::Zlib, - Some("zstd") => *slot = DebugInfoCompression::Zstd, - _ => return false, - }; - true + parse_string_enum(slot, v) } pub(crate) fn parse_mir_strip_debuginfo(slot: &mut MirStripDebugInfo, v: Option<&str>) -> bool { - match v { - Some("none") => *slot = MirStripDebugInfo::None, - Some("locals-in-tiny-functions") => *slot = MirStripDebugInfo::LocalsInTinyFunctions, - Some("all-locals") => *slot = MirStripDebugInfo::AllLocals, - _ => return false, - }; - true + parse_string_enum(slot, v) } pub(crate) fn parse_linker_flavor(slot: &mut Option, v: Option<&str>) -> bool { @@ -1607,27 +1622,13 @@ pub mod parse { slot: &mut InstrumentCoverage, v: Option<&str>, ) -> bool { - if v.is_some() { - let mut bool_arg = false; - if parse_bool(&mut bool_arg, v) { - *slot = if bool_arg { InstrumentCoverage::Yes } else { InstrumentCoverage::No }; - return true; - } - } - - let Some(v) = v else { - *slot = InstrumentCoverage::Yes; - return true; - }; - - // Parse values that have historically been accepted by stable compilers, - // even though they're currently just aliases for boolean values. - *slot = match v { - "all" => InstrumentCoverage::Yes, - "0" => InstrumentCoverage::No, - _ => return false, - }; - true + parse_string_enum_with_bool( + slot, + v, + Some(InstrumentCoverage::Yes), + Some(InstrumentCoverage::Yes), + Some(InstrumentCoverage::No), + ) } pub(crate) fn parse_codegen_retag_options( @@ -1781,21 +1782,13 @@ pub mod parse { } pub(crate) fn parse_lto(slot: &mut LtoCli, v: Option<&str>) -> bool { - if v.is_some() { - let mut bool_arg = None; - if parse_opt_bool(&mut bool_arg, v) { - *slot = if bool_arg.unwrap() { LtoCli::Yes } else { LtoCli::No }; - return true; - } - } - - *slot = match v { - None => LtoCli::NoParam, - Some("thin") => LtoCli::Thin, - Some("fat") => LtoCli::Fat, - Some(_) => return false, - }; - true + parse_string_enum_with_bool( + slot, + v, + Some(LtoCli::NoParam), + Some(LtoCli::Yes), + Some(LtoCli::No), + ) } pub(crate) fn parse_linker_plugin_lto(slot: &mut LinkerPluginLto, v: Option<&str>) -> bool { @@ -2044,23 +2037,13 @@ pub mod parse { slot: &mut CollapseMacroDebuginfo, v: Option<&str>, ) -> bool { - if v.is_some() { - let mut bool_arg = None; - if parse_opt_bool(&mut bool_arg, v) { - *slot = if bool_arg.unwrap() { - CollapseMacroDebuginfo::Yes - } else { - CollapseMacroDebuginfo::No - }; - return true; - } - } - - *slot = match v { - Some("external") => CollapseMacroDebuginfo::External, - _ => return false, - }; - true + parse_string_enum_with_bool( + slot, + v, + None, + Some(CollapseMacroDebuginfo::Yes), + Some(CollapseMacroDebuginfo::No), + ) } pub(crate) fn parse_proc_macro_execution_strategy( @@ -2123,12 +2106,7 @@ pub mod parse { } pub(crate) fn parse_function_return(slot: &mut FunctionReturn, v: Option<&str>) -> bool { - match v { - Some("keep") => *slot = FunctionReturn::Keep, - Some("thunk-extern") => *slot = FunctionReturn::ThunkExtern, - _ => return false, - } - true + parse_string_enum(slot, v) } pub(crate) fn parse_wasm_c_abi(_slot: &mut (), v: Option<&str>) -> bool { @@ -2209,7 +2187,7 @@ options! { "use Windows Control Flow Guard (default: no)"), debug_assertions: Option = (None, parse_opt_bool, [TRACKED], "explicitly enable the `cfg(debug_assertions)` directive"), - debuginfo: DebugInfo = (DebugInfo::None, parse_debuginfo, [TRACKED], + debuginfo: DebugInfo = (DebugInfo::None, parse_debuginfo, [TRACKED] [VALUES: DebugInfo::ALL_STR_VARIANTS], "debug info emission level (0-2, none, line-directives-only, \ line-tables-only, limited, or full; default: 0)"), default_linker_libraries: bool = (false, parse_bool, [UNTRACKED], @@ -2315,7 +2293,7 @@ options! { #[rustc_lint_opt_deny_field_access("use `Session::split_debuginfo` instead of this field")] split_debuginfo: Option = (None, parse_split_debuginfo, [TRACKED], "how to handle split-debuginfo, a platform-specific option"), - strip: Strip = (Strip::None, parse_strip, [UNTRACKED], + strip: Strip = (Strip::None, parse_strip, [UNTRACKED] [VALUES: Strip::STR_VARIANTS], "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), symbol_mangling_version: Option = (None, parse_symbol_mangling_version, [TRACKED], @@ -2413,7 +2391,7 @@ options! { "threshold to allow cross crate inlining of functions"), debug_info_type_line_numbers: bool = (false, parse_bool, [TRACKED], "emit type and line information for additional data types (default: no)"), - debuginfo_compression: DebugInfoCompression = (DebugInfoCompression::None, parse_debuginfo_compression, [TRACKED], + debuginfo_compression: DebugInfoCompression = (DebugInfoCompression::None, parse_debuginfo_compression, [TRACKED] [VALUES: DebugInfoCompression::STR_VARIANTS], "compress debug info sections (none, zlib, zstd, default: none)"), debuginfo_for_profiling: bool = (false, parse_bool, [TRACKED], "emit discriminators and other data necessary for AutoFDO"), @@ -2497,7 +2475,7 @@ options! { flatten_format_args: bool = (true, parse_bool, [TRACKED], "flatten nested format_args!() and literals into a simplified format_args!() call \ (default: yes)"), - fmt_debug: FmtDebug = (FmtDebug::Full, parse_fmt_debug, [TRACKED], + fmt_debug: FmtDebug = (FmtDebug::Full, parse_fmt_debug, [TRACKED] [VALUES: FmtDebug::STR_VARIANTS], "how detailed `#[derive(Debug)]` should be. `full` prints types recursively, \ `shallow` prints only type names, `none` prints nothing and disables `{:?}`. (default: `full`)"), force_intrinsic_fallback: bool = (false, parse_bool, [TRACKED], @@ -2505,7 +2483,7 @@ options! { the intrinsic in the codegen backend (default: no)."), force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED], "force all crates to be `rustc_private` unstable (default: no)"), - function_return: FunctionReturn = (FunctionReturn::default(), parse_function_return, [TRACKED], + function_return: FunctionReturn = (FunctionReturn::default(), parse_function_return, [TRACKED] [VALUES: FunctionReturn::STR_VARIANTS], "replace returns with jumps to `__x86_return_thunk` (default: `keep`)"), function_sections: Option = (None, parse_opt_bool, [TRACKED], "whether each function should go in its own section"), @@ -2643,7 +2621,7 @@ options! { mir_preserve_ub: bool = (false, parse_bool, [TRACKED], "keep place mention statements and reads in trivial SwitchInt terminators, which are interpreted \ e.g., by miri; implies -Zmir-opt-level=0 (default: no)"), - mir_strip_debuginfo: MirStripDebugInfo = (MirStripDebugInfo::None, parse_mir_strip_debuginfo, [TRACKED], + mir_strip_debuginfo: MirStripDebugInfo = (MirStripDebugInfo::None, parse_mir_strip_debuginfo, [TRACKED] [VALUES: MirStripDebugInfo::STR_VARIANTS], "Whether to remove some of the MIR debug info from methods. Default: None"), move_size_limit: Option = (None, parse_opt_number, [TRACKED], "the size at which the `large_assignments` lint starts to be emitted"), diff --git a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs index 4d64eec25d273..eff8afc78863f 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs @@ -33,7 +33,7 @@ pub(super) fn check_command_line(cx: &EarlyContext<'_>) { |diag| { diag.note(format!( "because of the command line `--{} clippy::restriction`", - level.as_str() + level.to_str() )); diag.help("enable the restriction lints you need individually"); }, diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index e5933d7c7a3f1..f719d62062364 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -644,7 +644,7 @@ impl LintMetadata { id: name, id_location: Some(lint.location), group: lint.category.name(), - level: lint.lint.default_level.as_str(), + level: lint.lint.default_level.to_str(), docs, version: lint.version, applicability, diff --git a/tests/ui/fmt/fmt_debug/invalid.stderr b/tests/ui/fmt/fmt_debug/invalid.stderr index fa6c938074445..de96bfccb99fb 100644 --- a/tests/ui/fmt/fmt_debug/invalid.stderr +++ b/tests/ui/fmt/fmt_debug/invalid.stderr @@ -1,2 +1,4 @@ -error: incorrect value `invalid-value` for unstable option `fmt-debug` - either `full`, `shallow`, or `none` was expected +error: incorrect value `invalid-value` for unstable option `fmt-debug` + | + = help: valid values are: `full`, `shallow`, `none`