From 82034115efa523a2fa470078901db92d2d4d246f Mon Sep 17 00:00:00 2001 From: Guillaume Date: Sat, 7 Mar 2026 09:45:29 +0100 Subject: [PATCH 01/17] Fix LegacyKeyValueFormat report from docker build: mips --- src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile | 4 ++-- src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile | 4 ++-- src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile | 4 ++-- src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile index 4a090dcdd5085..3d7f5f81fbec6 100644 --- a/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile @@ -26,5 +26,5 @@ ENV \ ENV HOSTS=mips-unknown-linux-gnu -ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS +ENV RUST_CONFIGURE_ARGS="--enable-extended --disable-docs" +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" diff --git a/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile index 18b0375f40080..3c798d737a48d 100644 --- a/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile @@ -26,5 +26,5 @@ ENV \ ENV HOSTS=mips64-unknown-linux-gnuabi64 -ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS +ENV RUST_CONFIGURE_ARGS="--enable-extended --disable-docs" +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" diff --git a/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile index 87407203880b4..32fd634555c2b 100644 --- a/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile @@ -26,5 +26,5 @@ ENV \ ENV HOSTS=mips64el-unknown-linux-gnuabi64 -ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS +ENV RUST_CONFIGURE_ARGS="--enable-extended --disable-docs" +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" diff --git a/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile index 553f6ea86b72d..73ab0cf5bab65 100644 --- a/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile @@ -26,5 +26,5 @@ ENV \ ENV HOSTS=mipsel-unknown-linux-gnu -ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS +ENV RUST_CONFIGURE_ARGS="--enable-extended --disable-docs" +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" From a22e9e0e026bd1d2da24041b40a4227a0b7d6616 Mon Sep 17 00:00:00 2001 From: randomicon00 <20146907+randomicon00@users.noreply.github.com> Date: Wed, 18 Mar 2026 21:56:01 -0400 Subject: [PATCH 02/17] docs: autogenerate compiler flag stubs by parsing options! macro --- src/tools/unstable-book-gen/src/main.rs | 611 +++++++++++++++++++++++- 1 file changed, 609 insertions(+), 2 deletions(-) diff --git a/src/tools/unstable-book-gen/src/main.rs b/src/tools/unstable-book-gen/src/main.rs index 16550f83003dc..4238d7afa341c 100644 --- a/src/tools/unstable-book-gen/src/main.rs +++ b/src/tools/unstable-book-gen/src/main.rs @@ -6,10 +6,12 @@ use std::fs::{self, write}; use std::path::Path; use tidy::diagnostics::RunningCheck; -use tidy::features::{Features, collect_env_vars, collect_lang_features, collect_lib_features}; +use tidy::features::{ + Feature, Features, Status, collect_env_vars, collect_lang_features, collect_lib_features, +}; use tidy::t; use tidy::unstable_book::{ - ENV_VARS_DIR, LANG_FEATURES_DIR, LIB_FEATURES_DIR, PATH_STR, + COMPILER_FLAGS_DIR, ENV_VARS_DIR, LANG_FEATURES_DIR, LIB_FEATURES_DIR, PATH_STR, collect_unstable_book_section_file_names, collect_unstable_feature_names, }; @@ -113,6 +115,510 @@ fn copy_recursive(from: &Path, to: &Path) { } } +fn collect_compiler_flags(compiler_path: &Path) -> Features { + let options_path = compiler_path.join("rustc_session/src/options.rs"); + let options_rs = t!(fs::read_to_string(&options_path), options_path); + parse_compiler_flags(&options_rs, &options_path) +} + +const DESCRIPTION_FIELD: usize = 3; +const REQUIRED_FIELDS: usize = 4; +const OPTIONAL_FIELDS: usize = 5; + +struct SourceBlock<'a> { + content: &'a str, + offset: usize, +} + +struct ParsedOptionEntry<'a> { + name: &'a str, + name_start: usize, + description: Option, + next_idx: usize, +} + +fn parse_compiler_flags(options_rs: &str, options_path: &Path) -> Features { + let options_block = find_options_block(options_rs, "UnstableOptions"); + let alphabetical_section = find_tidy_alphabetical_section(options_block); + let section_line_offset = line_number(options_rs, alphabetical_section.offset) - 1; + + let mut features = Features::new(); + let mut idx = 0; + + while idx < alphabetical_section.content.len() { + skip_ws_comments_and_attrs(alphabetical_section.content, &mut idx); + if idx >= alphabetical_section.content.len() { + break; + } + + let entry = parse_one_entry(alphabetical_section.content, idx); + idx = entry.next_idx; + + if entry.name == "help" { + continue; + } + + features.insert( + entry.name.to_owned(), + Feature { + level: Status::Unstable, + since: None, + has_gate_test: false, + tracking_issue: None, + file: options_path.to_path_buf(), + line: section_line_offset + + line_number(alphabetical_section.content, entry.name_start), + description: entry.description, + }, + ); + } + + features +} + +fn parse_one_entry(source: &str, start_idx: usize) -> ParsedOptionEntry<'_> { + let name_start = start_idx; + let name_end = + parse_ident_end(source, name_start).expect("expected an option name in UnstableOptions"); + let name = &source[name_start..name_end]; + let mut idx = name_end; + + skip_ws_comments(source, &mut idx); + expect_byte(source, idx, b':', &format!("expected `:` after option name `{name}`")); + idx += 1; + + idx = + find_char_outside_nested(source, idx, b'=').expect("expected `=` in UnstableOptions entry"); + idx += 1; + + skip_ws_comments(source, &mut idx); + expect_byte(source, idx, b'(', &format!("expected tuple payload for option `{name}`")); + + let tuple_start = idx; + let tuple_end = find_matching_delimiter(source, tuple_start, b'(', b')') + .expect("UnstableOptions tuple should be balanced"); + let next_idx = skip_past_entry_delimiter(source, tuple_end + 1, name); + + let description = if name == "help" { + None + } else { + let fields = split_top_level_fields(&source[tuple_start + 1..tuple_end]); + validate_option_fields(&fields, name); + // The `options!` macro layout is `(init, parse, [dep_tracking...], desc, ...)`. + Some(parse_string_literal( + fields.get(DESCRIPTION_FIELD).expect("option description should be present"), + )) + }; + + ParsedOptionEntry { name, name_start, description, next_idx } +} + +fn find_options_block<'a>(source: &'a str, struct_name: &str) -> SourceBlock<'a> { + let mut search_from = 0; + + while let Some(relative_start) = source[search_from..].find("options!") { + let macro_start = search_from + relative_start; + let open_brace = source[macro_start..] + .find('{') + .map(|relative| macro_start + relative) + .expect("options! invocation should contain `{`"); + let close_brace = find_matching_delimiter(source, open_brace, b'{', b'}') + .expect("options! invocation should have a matching `}`"); + let block = &source[open_brace + 1..close_brace]; + + if block.trim_start().starts_with(struct_name) { + return SourceBlock { content: block, offset: open_brace + 1 }; + } + + search_from = close_brace + 1; + } + + panic!("could not find `{struct_name}` options! block"); +} + +fn find_tidy_alphabetical_section(block: SourceBlock<'_>) -> SourceBlock<'_> { + let start_marker = "// tidy-alphabetical-start"; + let end_marker = "// tidy-alphabetical-end"; + + let section_start = block + .content + .find(start_marker) + .map(|start| start + start_marker.len()) + .expect("options! block should contain `// tidy-alphabetical-start`"); + let section_end = block.content[section_start..] + .find(end_marker) + .map(|end| section_start + end) + .expect("options! block should contain `// tidy-alphabetical-end`"); + + SourceBlock { + content: &block.content[section_start..section_end], + offset: block.offset + section_start, + } +} + +fn line_number(source: &str, offset: usize) -> usize { + source[..offset].bytes().filter(|&byte| byte == b'\n').count() + 1 +} + +fn expect_byte(source: &str, idx: usize, expected: u8, context: &str) { + assert_eq!(source.as_bytes().get(idx).copied(), Some(expected), "{context}"); +} + +fn skip_ws_comments_and_attrs(source: &str, idx: &mut usize) { + loop { + skip_ws_comments(source, idx); + + if source[*idx..].starts_with("#[") { + let attr_start = *idx + 1; + let attr_end = find_matching_delimiter(source, attr_start, b'[', b']') + .expect("attribute should have matching `]`"); + *idx = attr_end + 1; + continue; + } + + break; + } +} + +fn skip_ws_comments(source: &str, idx: &mut usize) { + loop { + while let Some(byte) = source.as_bytes().get(*idx) { + if byte.is_ascii_whitespace() { + *idx += 1; + } else { + break; + } + } + + if source[*idx..].starts_with("//") { + *idx = source[*idx..].find('\n').map_or(source.len(), |end| *idx + end + 1); + continue; + } + + if source[*idx..].starts_with("/*") { + *idx = skip_block_comment(source, *idx); + continue; + } + + break; + } +} + +fn skip_block_comment(source: &str, mut idx: usize) -> usize { + let mut depth = 1; + idx += 2; + + while idx < source.len() { + match source.as_bytes().get(idx..idx + 2) { + Some(b"/*") => { + depth += 1; + idx += 2; + } + Some(b"*/") => { + depth -= 1; + idx += 2; + if depth == 0 { + return idx; + } + } + _ => idx += 1, + } + } + + panic!("unterminated block comment"); +} + +fn parse_ident_end(source: &str, start: usize) -> Option { + let bytes = source.as_bytes(); + let first = *bytes.get(start)?; + if !(first == b'_' || first.is_ascii_alphabetic()) { + return None; + } + + let mut idx = start + 1; + while let Some(byte) = bytes.get(idx) { + if *byte == b'_' || byte.is_ascii_alphanumeric() { + idx += 1; + } else { + break; + } + } + + Some(idx) +} + +fn find_char_outside_nested(source: &str, start: usize, needle: u8) -> Option { + let mut idx = start; + let mut paren_depth = 0; + let mut bracket_depth = 0; + let mut brace_depth = 0; + + while idx < source.len() { + match source.as_bytes()[idx] { + b'/' if source[idx..].starts_with("//") => { + idx = source[idx..].find('\n').map_or(source.len(), |end| idx + end + 1); + } + b'/' if source[idx..].starts_with("/*") => idx = skip_block_comment(source, idx), + b'"' => idx = skip_string_literal(source, idx), + b'(' => { + paren_depth += 1; + idx += 1; + } + b')' => { + paren_depth -= 1; + idx += 1; + } + b'[' => { + bracket_depth += 1; + idx += 1; + } + b']' => { + bracket_depth -= 1; + idx += 1; + } + b'{' => { + brace_depth += 1; + idx += 1; + } + b'}' => { + brace_depth -= 1; + idx += 1; + } + byte if byte == needle + && paren_depth == 0 + && bracket_depth == 0 + && brace_depth == 0 => + { + return Some(idx); + } + _ => idx += 1, + } + } + + None +} + +fn find_matching_delimiter(source: &str, start: usize, open: u8, close: u8) -> Option { + let mut idx = start; + let mut depth = 0; + + while idx < source.len() { + match source.as_bytes()[idx] { + b'/' if source[idx..].starts_with("//") => { + idx = source[idx..].find('\n').map_or(source.len(), |end| idx + end + 1); + } + b'/' if source[idx..].starts_with("/*") => idx = skip_block_comment(source, idx), + b'"' => idx = skip_string_literal(source, idx), + byte if byte == open => { + depth += 1; + idx += 1; + } + byte if byte == close => { + depth -= 1; + if depth == 0 { + return Some(idx); + } + idx += 1; + } + _ => idx += 1, + } + } + + None +} + +fn split_top_level_fields(source: &str) -> Vec<&str> { + let mut fields = Vec::new(); + let mut field_start = 0; + let mut idx = 0; + let mut paren_depth = 0; + let mut bracket_depth = 0; + let mut brace_depth = 0; + + while idx < source.len() { + match source.as_bytes()[idx] { + b'/' if source[idx..].starts_with("//") => { + idx = source[idx..].find('\n').map_or(source.len(), |end| idx + end + 1); + } + b'/' if source[idx..].starts_with("/*") => idx = skip_block_comment(source, idx), + b'"' => idx = skip_string_literal(source, idx), + b'(' => { + paren_depth += 1; + idx += 1; + } + b')' => { + paren_depth -= 1; + idx += 1; + } + b'[' => { + bracket_depth += 1; + idx += 1; + } + b']' => { + bracket_depth -= 1; + idx += 1; + } + b'{' => { + brace_depth += 1; + idx += 1; + } + b'}' => { + brace_depth -= 1; + idx += 1; + } + b',' if paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 => { + fields.push(source[field_start..idx].trim()); + idx += 1; + field_start = idx; + } + _ => idx += 1, + } + } + + fields.push(source[field_start..].trim()); + fields +} + +fn validate_option_fields(fields: &[&str], name: &str) { + assert!( + matches!(fields.len(), REQUIRED_FIELDS | OPTIONAL_FIELDS), + "unexpected field count for option `{name}`: expected 4 or 5 fields, found {}", + fields.len() + ); + assert!( + fields[2].starts_with('[') && fields[2].ends_with(']'), + "expected dep-tracking field in option `{name}`, found `{}`", + fields[2] + ); + assert!( + looks_like_string_literal(fields[DESCRIPTION_FIELD]), + "expected description string literal in option `{name}`, found `{}`", + fields[DESCRIPTION_FIELD] + ); + + if let Some(extra_field) = fields.get(REQUIRED_FIELDS) { + assert!( + extra_field.trim_start().starts_with("is_deprecated_and_do_nothing:"), + "unexpected trailing field in option `{name}`: `{extra_field}`", + ); + } +} + +fn looks_like_string_literal(field: &str) -> bool { + let field = field.trim(); + (field.starts_with('"') && field.ends_with('"')) || parse_raw_string_literal(field).is_some() +} + +fn skip_past_entry_delimiter(source: &str, start: usize, name: &str) -> usize { + let mut idx = start; + skip_ws_comments(source, &mut idx); + + match source.as_bytes().get(idx).copied() { + Some(b',') => idx + 1, + None => idx, + Some(byte) => { + panic!("expected `,` after option entry `{name}`, found {:?}", char::from(byte)) + } + } +} + +fn skip_string_literal(source: &str, mut idx: usize) -> usize { + idx += 1; + + while idx < source.len() { + match source.as_bytes()[idx] { + b'\\' => { + idx += 1; + if idx < source.len() { + idx += 1; + } + } + b'"' => return idx + 1, + _ => idx += 1, + } + } + + panic!("unterminated string literal"); +} + +fn parse_string_literal(literal: &str) -> String { + let literal = literal.trim(); + + if let Some(raw_literal) = parse_raw_string_literal(literal) { + return raw_literal; + } + + let inner = literal + .strip_prefix('"') + .and_then(|value| value.strip_suffix('"')) + .expect("expected a string literal"); + let mut output = String::new(); + let mut chars = inner.chars().peekable(); + + while let Some(ch) = chars.next() { + if ch != '\\' { + output.push(ch); + continue; + } + + let escaped = chars.next().expect("unterminated string escape"); + match escaped { + '\n' => while chars.next_if(|ch| ch.is_whitespace()).is_some() {}, + '\r' => { + let _ = chars.next_if_eq(&'\n'); + while chars.next_if(|ch| ch.is_whitespace()).is_some() {} + } + '"' => output.push('"'), + '\'' => output.push('\''), + '\\' => output.push('\\'), + 'n' => output.push('\n'), + 'r' => output.push('\r'), + 't' => output.push('\t'), + '0' => output.push('\0'), + 'x' => { + let hi = chars.next().expect("missing first hex digit in escape"); + let lo = chars.next().expect("missing second hex digit in escape"); + let byte = u8::from_str_radix(&format!("{hi}{lo}"), 16) + .expect("invalid hex escape in string literal"); + output.push(char::from(byte)); + } + 'u' => { + assert_eq!(chars.next(), Some('{'), "expected `{{` after `\\u`"); + let mut digits = String::new(); + for ch in chars.by_ref() { + if ch == '}' { + break; + } + digits.push(ch); + } + let scalar = + u32::from_str_radix(&digits, 16).expect("invalid unicode escape in string"); + output.push(char::from_u32(scalar).expect("unicode escape should be valid")); + } + _ => panic!("unsupported escape in string literal"), + } + } + + output +} + +fn parse_raw_string_literal(literal: &str) -> Option { + let rest = literal.strip_prefix('r')?; + let hashes = rest.bytes().take_while(|&byte| byte == b'#').count(); + let quote_idx = 1 + hashes; + + if literal.as_bytes().get(quote_idx) != Some(&b'"') { + return None; + } + + let suffix = format!("\"{}", "#".repeat(hashes)); + let content = literal[quote_idx + 1..] + .strip_suffix(&suffix) + .expect("raw string literal should have a matching terminator"); + + Some(content.to_owned()) +} + fn main() { let library_path_str = env::args_os().nth(1).expect("library/ path required"); let compiler_path_str = env::args_os().nth(2).expect("compiler/ path required"); @@ -129,6 +635,7 @@ fn main() { .filter(|&(ref name, _)| !lang_features.contains_key(name)) .collect(); let env_vars = collect_env_vars(compiler_path); + let compiler_flags = collect_compiler_flags(compiler_path); let doc_src_path = src_path.join(PATH_STR); @@ -144,9 +651,109 @@ fn main() { &dest_path.join(LIB_FEATURES_DIR), &lib_features, ); + generate_feature_files( + &doc_src_path.join(COMPILER_FLAGS_DIR), + &dest_path.join(COMPILER_FLAGS_DIR), + &compiler_flags, + ); generate_env_files(&doc_src_path.join(ENV_VARS_DIR), &dest_path.join(ENV_VARS_DIR), &env_vars); copy_recursive(&doc_src_path, &dest_path); generate_summary(&dest_path, &lang_features, &lib_features); } + +#[cfg(test)] +mod tests { + use std::path::{Path, PathBuf}; + + use super::{parse_compiler_flags, parse_one_entry, skip_ws_comments_and_attrs}; + + #[test] + fn parses_unstable_options_entries() { + let options_rs = r#" +options! { + UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, "Z", "unstable", + + // tidy-alphabetical-start + #[rustc_lint_opt_deny_field_access("test attr")] + allow_features: Option> = (None, parse_opt_comma_list, [TRACKED], + "only allow the listed language features to be enabled in code (comma separated)"), + dump_mir: Option = (None, parse_opt_string, [UNTRACKED], + "dump MIR state to file. + `val` is used to select which passes and functions to dump."), + join_lines: bool = (false, parse_bool, [TRACKED], + "join \ + continued lines"), + help: bool = (false, parse_no_value, [UNTRACKED], "Print unstable compiler options"), + // tidy-alphabetical-end +} +"#; + + let features = parse_compiler_flags(options_rs, Path::new("options.rs")); + + assert!(features.contains_key("allow_features")); + assert!(features.contains_key("dump_mir")); + assert!(features.contains_key("join_lines")); + assert!(!features.contains_key("help")); + + assert_eq!( + features["dump_mir"].description.as_deref(), + Some( + "dump MIR state to file.\n `val` is used to select which passes and functions to dump." + ), + ); + assert_eq!(features["join_lines"].description.as_deref(), Some("join continued lines"),); + assert_eq!(features["allow_features"].file, PathBuf::from("options.rs")); + assert_eq!(features["allow_features"].line, 7); + } + + #[test] + fn parse_one_entry_skips_help_description_and_advances() { + let section = r#" +help: bool = (false, parse_no_value, [UNTRACKED], "Print unstable compiler options"), +join_lines: bool = (false, parse_bool, [TRACKED], "join \ + continued lines"), +"#; + let section = section.trim_start(); + + let help_entry = parse_one_entry(section, 0); + assert_eq!(help_entry.name, "help"); + assert!(help_entry.description.is_none()); + + let mut next_idx = help_entry.next_idx; + skip_ws_comments_and_attrs(section, &mut next_idx); + let next_entry = parse_one_entry(section, next_idx); + + assert_eq!(next_entry.name, "join_lines"); + assert_eq!(next_entry.description.as_deref(), Some("join continued lines"),); + } + + #[test] + fn parse_one_entry_accepts_optional_trailing_metadata() { + let entry = r#" +deprecated_flag: bool = (false, parse_no_value, [UNTRACKED], "deprecated flag", + is_deprecated_and_do_nothing: true), +"#; + let entry = entry.trim_start(); + + let parsed = parse_one_entry(entry, 0); + assert_eq!(parsed.name, "deprecated_flag"); + assert_eq!(parsed.description.as_deref(), Some("deprecated flag")); + } + + #[test] + fn parses_real_unstable_options_file() { + let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); + let options_path = manifest_dir.join("../../../compiler/rustc_session/src/options.rs"); + let options_rs = std::fs::read_to_string(&options_path).unwrap(); + let features = parse_compiler_flags(&options_rs, &options_path); + + assert!(features.contains_key("allow_features")); + assert!(features.contains_key("dump_mir")); + assert!(features.contains_key("unstable_options")); + assert!(!features.contains_key("help")); + assert!(features["dump_mir"].line > 0); + assert!(features["dump_mir"].description.as_deref().unwrap().starts_with("dump MIR state")); + } +} From 83a21a5e7129b62e4c1944affb906c0751a6cfaf Mon Sep 17 00:00:00 2001 From: randomicon00 <20146907+randomicon00@users.noreply.github.com> Date: Wed, 18 Mar 2026 21:59:48 -0400 Subject: [PATCH 03/17] fix: move tests to separate file and avoid tidy markers in strings --- src/tools/unstable-book-gen/src/main.rs | 94 +----------------------- src/tools/unstable-book-gen/src/tests.rs | 90 +++++++++++++++++++++++ 2 files changed, 91 insertions(+), 93 deletions(-) create mode 100644 src/tools/unstable-book-gen/src/tests.rs diff --git a/src/tools/unstable-book-gen/src/main.rs b/src/tools/unstable-book-gen/src/main.rs index 4238d7afa341c..12b35a11c0d66 100644 --- a/src/tools/unstable-book-gen/src/main.rs +++ b/src/tools/unstable-book-gen/src/main.rs @@ -664,96 +664,4 @@ fn main() { } #[cfg(test)] -mod tests { - use std::path::{Path, PathBuf}; - - use super::{parse_compiler_flags, parse_one_entry, skip_ws_comments_and_attrs}; - - #[test] - fn parses_unstable_options_entries() { - let options_rs = r#" -options! { - UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, "Z", "unstable", - - // tidy-alphabetical-start - #[rustc_lint_opt_deny_field_access("test attr")] - allow_features: Option> = (None, parse_opt_comma_list, [TRACKED], - "only allow the listed language features to be enabled in code (comma separated)"), - dump_mir: Option = (None, parse_opt_string, [UNTRACKED], - "dump MIR state to file. - `val` is used to select which passes and functions to dump."), - join_lines: bool = (false, parse_bool, [TRACKED], - "join \ - continued lines"), - help: bool = (false, parse_no_value, [UNTRACKED], "Print unstable compiler options"), - // tidy-alphabetical-end -} -"#; - - let features = parse_compiler_flags(options_rs, Path::new("options.rs")); - - assert!(features.contains_key("allow_features")); - assert!(features.contains_key("dump_mir")); - assert!(features.contains_key("join_lines")); - assert!(!features.contains_key("help")); - - assert_eq!( - features["dump_mir"].description.as_deref(), - Some( - "dump MIR state to file.\n `val` is used to select which passes and functions to dump." - ), - ); - assert_eq!(features["join_lines"].description.as_deref(), Some("join continued lines"),); - assert_eq!(features["allow_features"].file, PathBuf::from("options.rs")); - assert_eq!(features["allow_features"].line, 7); - } - - #[test] - fn parse_one_entry_skips_help_description_and_advances() { - let section = r#" -help: bool = (false, parse_no_value, [UNTRACKED], "Print unstable compiler options"), -join_lines: bool = (false, parse_bool, [TRACKED], "join \ - continued lines"), -"#; - let section = section.trim_start(); - - let help_entry = parse_one_entry(section, 0); - assert_eq!(help_entry.name, "help"); - assert!(help_entry.description.is_none()); - - let mut next_idx = help_entry.next_idx; - skip_ws_comments_and_attrs(section, &mut next_idx); - let next_entry = parse_one_entry(section, next_idx); - - assert_eq!(next_entry.name, "join_lines"); - assert_eq!(next_entry.description.as_deref(), Some("join continued lines"),); - } - - #[test] - fn parse_one_entry_accepts_optional_trailing_metadata() { - let entry = r#" -deprecated_flag: bool = (false, parse_no_value, [UNTRACKED], "deprecated flag", - is_deprecated_and_do_nothing: true), -"#; - let entry = entry.trim_start(); - - let parsed = parse_one_entry(entry, 0); - assert_eq!(parsed.name, "deprecated_flag"); - assert_eq!(parsed.description.as_deref(), Some("deprecated flag")); - } - - #[test] - fn parses_real_unstable_options_file() { - let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); - let options_path = manifest_dir.join("../../../compiler/rustc_session/src/options.rs"); - let options_rs = std::fs::read_to_string(&options_path).unwrap(); - let features = parse_compiler_flags(&options_rs, &options_path); - - assert!(features.contains_key("allow_features")); - assert!(features.contains_key("dump_mir")); - assert!(features.contains_key("unstable_options")); - assert!(!features.contains_key("help")); - assert!(features["dump_mir"].line > 0); - assert!(features["dump_mir"].description.as_deref().unwrap().starts_with("dump MIR state")); - } -} +mod tests; diff --git a/src/tools/unstable-book-gen/src/tests.rs b/src/tools/unstable-book-gen/src/tests.rs new file mode 100644 index 0000000000000..98008a45be629 --- /dev/null +++ b/src/tools/unstable-book-gen/src/tests.rs @@ -0,0 +1,90 @@ +use std::path::{Path, PathBuf}; + +use super::{parse_compiler_flags, parse_one_entry, skip_ws_comments_and_attrs}; + +#[test] +fn parses_unstable_options_entries() { + let tidy_start = "// tidy-alphabetical-start"; + let tidy_end = "// tidy-alphabetical-end"; + let options_rs = format!( + "\n\ + options! {{\n\ + \x20 UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, \"Z\", \"unstable\",\n\ + \n\ + \x20 {tidy_start}\n\ + \x20 #[rustc_lint_opt_deny_field_access(\"test attr\")]\n\ + \x20 allow_features: Option> = (None, parse_opt_comma_list, [TRACKED],\n\ + \x20 \"only allow the listed language features to be enabled in code (comma separated)\"),\n\ + \x20 dump_mir: Option = (None, parse_opt_string, [UNTRACKED],\n\ + \x20 \"dump MIR state to file.\n\ + \x20 `val` is used to select which passes and functions to dump.\"),\n\ + \x20 join_lines: bool = (false, parse_bool, [TRACKED],\n\ + \x20 \"join \\\n\ + \x20 continued lines\"),\n\ + \x20 help: bool = (false, parse_no_value, [UNTRACKED], \"Print unstable compiler options\"),\n\ + \x20 {tidy_end}\n\ + }}\n" + ); + + let features = parse_compiler_flags(&options_rs, Path::new("options.rs")); + + assert!(features.contains_key("allow_features")); + assert!(features.contains_key("dump_mir")); + assert!(features.contains_key("join_lines")); + assert!(!features.contains_key("help")); + + assert_eq!( + features["dump_mir"].description.as_deref(), + Some( + "dump MIR state to file.\n `val` is used to select which passes and functions to dump." + ), + ); + assert_eq!(features["join_lines"].description.as_deref(), Some("join continued lines"),); + assert_eq!(features["allow_features"].file, PathBuf::from("options.rs")); + assert_eq!(features["allow_features"].line, 7); +} + +#[test] +fn parse_one_entry_skips_help_description_and_advances() { + let section = "\ +help: bool = (false, parse_no_value, [UNTRACKED], \"Print unstable compiler options\"),\n\ +join_lines: bool = (false, parse_bool, [TRACKED], \"join \\\n\ + continued lines\"),\n"; + + let help_entry = parse_one_entry(section, 0); + assert_eq!(help_entry.name, "help"); + assert!(help_entry.description.is_none()); + + let mut next_idx = help_entry.next_idx; + skip_ws_comments_and_attrs(section, &mut next_idx); + let next_entry = parse_one_entry(section, next_idx); + + assert_eq!(next_entry.name, "join_lines"); + assert_eq!(next_entry.description.as_deref(), Some("join continued lines"),); +} + +#[test] +fn parse_one_entry_accepts_optional_trailing_metadata() { + let entry = "\ +deprecated_flag: bool = (false, parse_no_value, [UNTRACKED], \"deprecated flag\",\n\ + is_deprecated_and_do_nothing: true),\n"; + + let parsed = parse_one_entry(entry, 0); + assert_eq!(parsed.name, "deprecated_flag"); + assert_eq!(parsed.description.as_deref(), Some("deprecated flag")); +} + +#[test] +fn parses_real_unstable_options_file() { + let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); + let options_path = manifest_dir.join("../../../compiler/rustc_session/src/options.rs"); + let options_rs = std::fs::read_to_string(&options_path).unwrap(); + let features = parse_compiler_flags(&options_rs, &options_path); + + assert!(features.contains_key("allow_features")); + assert!(features.contains_key("dump_mir")); + assert!(features.contains_key("unstable_options")); + assert!(!features.contains_key("help")); + assert!(features["dump_mir"].line > 0); + assert!(features["dump_mir"].description.as_deref().unwrap().starts_with("dump MIR state")); +} From b4f2c8a08e8752f016978b96d09d473bc7bc1a7b Mon Sep 17 00:00:00 2001 From: randomicon00 <20146907+randomicon00@users.noreply.github.com> Date: Sat, 21 Mar 2026 11:31:41 -0400 Subject: [PATCH 04/17] docs: parse unstable options with syn --- Cargo.lock | 2 + compiler/rustc_session/src/options.rs | 586 +----------------- .../rustc_session/src/options/unstable.rs | 585 +++++++++++++++++ src/tools/unstable-book-gen/Cargo.toml | 2 + src/tools/unstable-book-gen/src/main.rs | 571 ++++------------- src/tools/unstable-book-gen/src/tests.rs | 98 ++- 6 files changed, 758 insertions(+), 1086 deletions(-) create mode 100644 compiler/rustc_session/src/options/unstable.rs diff --git a/Cargo.lock b/Cargo.lock index 539f66ffb3bb3..1be93aac6e086 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6088,6 +6088,8 @@ name = "unstable-book-gen" version = "0.1.0" dependencies = [ "num-traits", + "proc-macro2", + "syn 2.0.110", "tidy", ] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index fb1b3c8679481..836f7d1cb456f 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2213,588 +2213,4 @@ options! { // - src/doc/rustc/src/codegen-options/index.md } -options! { - UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, "Z", "unstable", - - // If you add a new option, please update: - // - compiler/rustc_interface/src/tests.rs - // - src/doc/unstable-book/src/compiler-flags - - // tidy-alphabetical-start - allow_features: Option> = (None, parse_opt_comma_list, [TRACKED], - "only allow the listed language features to be enabled in code (comma separated)"), - always_encode_mir: bool = (false, parse_bool, [TRACKED], - "encode MIR of all functions into the crate metadata (default: no)"), - annotate_moves: AnnotateMoves = (AnnotateMoves::Disabled, parse_annotate_moves, [TRACKED], - "emit debug info for compiler-generated move and copy operations \ - to make them visible in profilers. Can be a boolean or a size limit in bytes (default: disabled)"), - assert_incr_state: Option = (None, parse_opt_string, [UNTRACKED], - "assert that the incremental cache is in given state: \ - either `loaded` or `not-loaded`."), - assume_incomplete_release: bool = (false, parse_bool, [TRACKED], - "make cfg(version) treat the current version as incomplete (default: no)"), - autodiff: Vec = (Vec::new(), parse_autodiff, [TRACKED], - "a list of autodiff flags to enable - Mandatory setting: - `=Enable` - Optional extra settings: - `=PrintTA` - `=PrintAA` - `=PrintPerf` - `=PrintSteps` - `=PrintModBefore` - `=PrintModAfter` - `=PrintModFinal` - `=PrintPasses`, - `=NoPostopt` - `=LooseTypes` - `=Inline` - Multiple options can be combined with commas."), - #[rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field")] - binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], - "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ - (default: no)"), - box_noalias: bool = (true, parse_bool, [TRACKED], - "emit noalias metadata for box (default: yes)"), - branch_protection: Option = (None, parse_branch_protection, [TRACKED TARGET_MODIFIER], - "set options for branch target identification and pointer authentication on AArch64"), - build_sdylib_interface: bool = (false, parse_bool, [UNTRACKED], - "whether the stable interface is being built"), - cache_proc_macros: bool = (false, parse_bool, [TRACKED], - "cache the results of derive proc macro invocations (potentially unsound!) (default: no"), - cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED], - "instrument control-flow architecture protection"), - check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED], - "show all expected values in check-cfg diagnostics (default: no)"), - checksum_hash_algorithm: Option = (None, parse_cargo_src_file_hash, [TRACKED], - "hash algorithm of source files used to check freshness in cargo (`blake3` or `sha256`)"), - codegen_backend: Option = (None, parse_opt_string, [TRACKED], - "the backend to use"), - codegen_source_order: bool = (false, parse_bool, [UNTRACKED], - "emit mono items in the order of spans in source files (default: no)"), - contract_checks: Option = (None, parse_opt_bool, [TRACKED], - "emit runtime checks for contract pre- and post-conditions (default: no)"), - coverage_options: CoverageOptions = (CoverageOptions::default(), parse_coverage_options, [TRACKED], - "control details of coverage instrumentation"), - crate_attr: Vec = (Vec::new(), parse_string_push, [TRACKED], - "inject the given attribute in the crate"), - cross_crate_inline_threshold: InliningThreshold = (InliningThreshold::Sometimes(100), parse_inlining_threshold, [TRACKED], - "threshold to allow cross crate inlining of functions"), - debug_info_for_profiling: bool = (false, parse_bool, [TRACKED], - "emit discriminators and other data necessary for AutoFDO"), - 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], - "compress debug info sections (none, zlib, zstd, default: none)"), - deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED], - "deduplicate identical diagnostics (default: yes)"), - default_visibility: Option = (None, parse_opt_symbol_visibility, [TRACKED], - "overrides the `default_visibility` setting of the target"), - dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED], - "in dep-info output, omit targets for tracking dependencies of the dep-info files \ - themselves (default: no)"), - direct_access_external_data: Option = (None, parse_opt_bool, [TRACKED], - "Direct or use GOT indirect to reference external data symbols"), - dual_proc_macros: bool = (false, parse_bool, [TRACKED], - "load proc macros for both target and host, but only link to the target (default: no)"), - dump_dep_graph: bool = (false, parse_bool, [UNTRACKED], - "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) \ - (default: no)"), - dump_mir: Option = (None, parse_opt_string, [UNTRACKED], - "dump MIR state to file. - `val` is used to select which passes and functions to dump. For example: - `all` matches all passes and functions, - `foo` matches all passes for functions whose name contains 'foo', - `foo & ConstProp` only the 'ConstProp' pass for function names containing 'foo', - `foo | bar` all passes for function names containing 'foo' or 'bar'."), - dump_mir_dataflow: bool = (false, parse_bool, [UNTRACKED], - "in addition to `.mir` files, create graphviz `.dot` files with dataflow results \ - (default: no)"), - dump_mir_dir: String = ("mir_dump".to_string(), parse_string, [UNTRACKED], - "the directory the MIR is dumped into (default: `mir_dump`)"), - dump_mir_exclude_alloc_bytes: bool = (false, parse_bool, [UNTRACKED], - "exclude the raw bytes of allocations when dumping MIR (used in tests) (default: no)"), - dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED], - "exclude the pass number when dumping MIR (used in tests) (default: no)"), - dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], - "in addition to `.mir` files, create graphviz `.dot` files (default: no)"), - dump_mono_stats: SwitchWithOptPath = (SwitchWithOptPath::Disabled, - parse_switch_with_opt_path, [UNTRACKED], - "output statistics about monomorphization collection"), - dump_mono_stats_format: DumpMonoStatsFormat = (DumpMonoStatsFormat::Markdown, parse_dump_mono_stats, [UNTRACKED], - "the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)"), - #[rustc_lint_opt_deny_field_access("use `Session::dwarf_version` instead of this field")] - dwarf_version: Option = (None, parse_opt_number, [TRACKED], - "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), - dylib_lto: bool = (false, parse_bool, [UNTRACKED], - "enables LTO for dylib crate type"), - eagerly_emit_delayed_bugs: bool = (false, parse_bool, [UNTRACKED], - "emit delayed bugs eagerly as errors instead of stashing them and emitting \ - them only if an error has not been emitted"), - ehcont_guard: bool = (false, parse_bool, [TRACKED], - "generate Windows EHCont Guard tables"), - embed_metadata: bool = (true, parse_bool, [TRACKED], - "embed metadata in rlibs and dylibs (default: yes)"), - embed_source: bool = (false, parse_bool, [TRACKED], - "embed source text in DWARF debug sections (default: no)"), - emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], - "emit a section containing stack size metadata (default: no)"), - emscripten_wasm_eh: bool = (true, parse_bool, [TRACKED], - "Use WebAssembly error handling for wasm32-unknown-emscripten"), - enforce_type_length_limit: bool = (false, parse_bool, [TRACKED], - "enforce the type length limit when monomorphizing instances in codegen"), - experimental_default_bounds: bool = (false, parse_bool, [TRACKED], - "enable default bounds for experimental group of auto traits"), - export_executable_symbols: bool = (false, parse_bool, [TRACKED], - "export symbols from executables, as if they were dynamic libraries"), - external_clangrt: bool = (false, parse_bool, [UNTRACKED], - "rely on user specified linker commands to find clangrt"), - extra_const_ub_checks: bool = (false, parse_bool, [TRACKED], - "turns on more checks to detect const UB, which can be slow (default: no)"), - #[rustc_lint_opt_deny_field_access("use `Session::fewer_names` instead of this field")] - fewer_names: Option = (None, parse_opt_bool, [TRACKED], - "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ - (default: no)"), - fixed_x18: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], - "make the x18 register reserved on AArch64 (default: no)"), - 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], - "how detailed `#[derive(Debug)]` should be. `full` prints types recursively, \ - `shallow` prints only type names, `none` prints nothing and disables `{:?}`. (default: `full`)"), - 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], - "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"), - future_incompat_test: bool = (false, parse_bool, [UNTRACKED], - "forces all lints to be future incompatible, used for internal testing (default: no)"), - graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED], - "use dark-themed colors in graphviz output (default: no)"), - graphviz_font: String = ("Courier, monospace".to_string(), parse_string, [UNTRACKED], - "use the given `fontname` in graphviz output; can be overridden by setting \ - environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"), - has_thread_local: Option = (None, parse_opt_bool, [TRACKED], - "explicitly enable the `cfg(target_thread_local)` directive"), - help: bool = (false, parse_no_value, [UNTRACKED], "Print unstable compiler options"), - higher_ranked_assumptions: bool = (false, parse_bool, [TRACKED], - "allow deducing higher-ranked outlives assumptions from coroutines when proving auto traits"), - hint_mostly_unused: bool = (false, parse_bool, [TRACKED], - "hint that most of this crate will go unused, to minimize work for uncalled functions"), - human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], - "generate human-readable, predictable names for codegen units (default: no)"), - identify_regions: bool = (false, parse_bool, [UNTRACKED], - "display unnamed regions as `'`, using a non-ident unique id (default: no)"), - ignore_directory_in_diagnostics_source_blocks: Vec = (Vec::new(), parse_string_push, [UNTRACKED], - "do not display the source code block in diagnostics for files in the directory"), - incremental_ignore_spans: bool = (false, parse_bool, [TRACKED], - "ignore spans during ICH computation -- used for testing (default: no)"), - incremental_info: bool = (false, parse_bool, [UNTRACKED], - "print high-level information about incremental reuse (or the lack thereof) \ - (default: no)"), - incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED], - "verify extended properties for incr. comp. (default: no): - - hashes of green query instances - - hash collisions of query keys - - hash collisions when creating dep-nodes"), - indirect_branch_cs_prefix: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], - "add `cs` prefix to `call` and `jmp` to indirect thunks (default: no)"), - inline_llvm: bool = (true, parse_bool, [TRACKED], - "enable LLVM inlining (default: yes)"), - inline_mir: Option = (None, parse_opt_bool, [TRACKED], - "enable MIR inlining (default: no)"), - inline_mir_forwarder_threshold: Option = (None, parse_opt_number, [TRACKED], - "inlining threshold when the caller is a simple forwarding function (default: 30)"), - inline_mir_hint_threshold: Option = (None, parse_opt_number, [TRACKED], - "inlining threshold for functions with inline hint (default: 100)"), - inline_mir_preserve_debug: Option = (None, parse_opt_bool, [TRACKED], - "when MIR inlining, whether to preserve debug info for callee variables \ - (default: preserve for debuginfo != None, otherwise remove)"), - inline_mir_threshold: Option = (None, parse_opt_number, [TRACKED], - "a default MIR inlining threshold (default: 50)"), - input_stats: bool = (false, parse_bool, [UNTRACKED], - "print some statistics about AST and HIR (default: no)"), - instrument_mcount: bool = (false, parse_bool, [TRACKED], - "insert function instrument code for mcount-based tracing (default: no)"), - instrument_xray: Option = (None, parse_instrument_xray, [TRACKED], - "insert function instrument code for XRay-based tracing (default: no) - Optional extra settings: - `=always` - `=never` - `=ignore-loops` - `=instruction-threshold=N` - `=skip-entry` - `=skip-exit` - Multiple options can be combined with commas."), - large_data_threshold: Option = (None, parse_opt_number, [TRACKED], - "set the threshold for objects to be stored in a \"large data\" section \ - (only effective with -Ccode-model=medium, default: 65536)"), - layout_seed: Option = (None, parse_opt_number, [TRACKED], - "seed layout randomization"), - link_directives: bool = (true, parse_bool, [TRACKED], - "honor #[link] directives in the compiled crate (default: yes)"), - link_native_libraries: bool = (true, parse_bool, [UNTRACKED], - "link native libraries in the linker invocation (default: yes)"), - link_only: bool = (false, parse_bool, [TRACKED], - "link the `.rlink` file generated by `-Z no-link` (default: no)"), - lint_llvm_ir: bool = (false, parse_bool, [TRACKED], - "lint LLVM IR (default: no)"), - lint_mir: bool = (false, parse_bool, [UNTRACKED], - "lint MIR before and after each transformation"), - llvm_module_flag: Vec<(String, u32, String)> = (Vec::new(), parse_llvm_module_flag, [TRACKED], - "a list of module flags to pass to LLVM (space separated)"), - llvm_plugins: Vec = (Vec::new(), parse_list, [TRACKED], - "a list LLVM plugins to enable (space separated)"), - llvm_time_trace: bool = (false, parse_bool, [UNTRACKED], - "generate JSON tracing data file from LLVM data (default: no)"), - location_detail: LocationDetail = (LocationDetail::all(), parse_location_detail, [TRACKED], - "what location details should be tracked when using caller_location, either \ - `none`, or a comma separated list of location details, for which \ - valid options are `file`, `line`, and `column` (default: `file,line,column`)"), - ls: Vec = (Vec::new(), parse_list, [UNTRACKED], - "decode and print various parts of the crate metadata for a library crate \ - (space separated)"), - macro_backtrace: bool = (false, parse_bool, [UNTRACKED], - "show macro backtraces (default: no)"), - macro_stats: bool = (false, parse_bool, [UNTRACKED], - "print some statistics about macro expansions (default: no)"), - maximal_hir_to_mir_coverage: bool = (false, parse_bool, [TRACKED], - "save as much information as possible about the correspondence between MIR and HIR \ - as source scopes (default: no)"), - merge_functions: Option = (None, parse_merge_functions, [TRACKED], - "control the operation of the MergeFunctions LLVM pass, taking \ - the same values as the target option of the same name"), - meta_stats: bool = (false, parse_bool, [UNTRACKED], - "gather metadata statistics (default: no)"), - metrics_dir: Option = (None, parse_opt_pathbuf, [UNTRACKED], - "the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"), - min_function_alignment: Option = (None, parse_align, [TRACKED], - "align all functions to at least this many bytes. Must be a power of 2"), - min_recursion_limit: Option = (None, parse_opt_number, [TRACKED], - "set a minimum recursion limit (final limit = max(this, recursion_limit_from_crate))"), - mir_emit_retag: bool = (false, parse_bool, [TRACKED], - "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ - (default: no)"), - mir_enable_passes: Vec<(String, bool)> = (Vec::new(), parse_list_with_polarity, [TRACKED], - "use like `-Zmir-enable-passes=+DestinationPropagation,-InstSimplify`. Forces the \ - specified passes to be enabled, overriding all other checks. In particular, this will \ - enable unsound (known-buggy and hence usually disabled) passes without further warning! \ - Passes that are not specified are enabled or disabled by other flags as usual."), - mir_include_spans: MirIncludeSpans = (MirIncludeSpans::default(), parse_mir_include_spans, [UNTRACKED], - "include extra comments in mir pretty printing, like line numbers and statement indices, \ - details about types, etc. (boolean for all passes, 'nll' to enable in NLL MIR only, default: 'nll')"), - mir_opt_bisect_limit: Option = (None, parse_opt_number, [TRACKED], - "limit the number of MIR optimization pass executions (global across all bodies). \ - Pass executions after this limit are skipped and reported. (default: no limit)"), - #[rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field")] - mir_opt_level: Option = (None, parse_opt_number, [TRACKED], - "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), - 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], - "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"), - mutable_noalias: bool = (true, parse_bool, [TRACKED], - "emit noalias metadata for mutable references (default: yes)"), - namespaced_crates: bool = (false, parse_bool, [TRACKED], - "allow crates to be namespaced by other crates (default: no)"), - next_solver: NextSolverConfig = (NextSolverConfig::default(), parse_next_solver_config, [TRACKED], - "enable and configure the next generation trait solver used by rustc"), - nll_facts: bool = (false, parse_bool, [UNTRACKED], - "dump facts from NLL analysis into side files (default: no)"), - nll_facts_dir: String = ("nll-facts".to_string(), parse_string, [UNTRACKED], - "the directory the NLL facts are dumped into (default: `nll-facts`)"), - no_analysis: bool = (false, parse_no_value, [UNTRACKED], - "parse and expand the source, but run no analysis"), - no_codegen: bool = (false, parse_no_value, [TRACKED_NO_CRATE_HASH], - "run all passes except codegen; no output"), - no_generate_arange_section: bool = (false, parse_no_value, [TRACKED], - "omit DWARF address ranges that give faster lookups"), - no_implied_bounds_compat: bool = (false, parse_bool, [TRACKED], - "disable the compatibility version of the `implied_bounds_ty` query"), - no_leak_check: bool = (false, parse_no_value, [UNTRACKED], - "disable the 'leak check' for subtyping; unsound, but useful for tests"), - no_link: bool = (false, parse_no_value, [TRACKED], - "compile without linking"), - no_parallel_backend: bool = (false, parse_no_value, [UNTRACKED], - "run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"), - no_profiler_runtime: bool = (false, parse_no_value, [TRACKED], - "prevent automatic injection of the profiler_builtins crate"), - no_steal_thir: bool = (false, parse_bool, [UNTRACKED], - "don't steal the THIR when we're done with it; useful for rustc drivers (default: no)"), - no_trait_vptr: bool = (false, parse_no_value, [TRACKED], - "disable generation of trait vptr in vtable for upcasting"), - no_unique_section_names: bool = (false, parse_bool, [TRACKED], - "do not use unique names for text and data sections when -Z function-sections is used"), - normalize_docs: bool = (false, parse_bool, [TRACKED], - "normalize associated items in rustdoc when generating documentation"), - offload: Vec = (Vec::new(), parse_offload, [TRACKED], - "a list of offload flags to enable - Mandatory setting: - `=Enable` - Currently the only option available"), - on_broken_pipe: OnBrokenPipe = (OnBrokenPipe::Default, parse_on_broken_pipe, [TRACKED], - "behavior of std::io::ErrorKind::BrokenPipe (SIGPIPE)"), - osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], - "pass `-install_name @rpath/...` to the macOS linker (default: no)"), - packed_bundled_libs: bool = (false, parse_bool, [TRACKED], - "change rlib format to store native libraries as archives"), - panic_abort_tests: bool = (false, parse_bool, [TRACKED], - "support compiling tests with panic=abort (default: no)"), - panic_in_drop: PanicStrategy = (PanicStrategy::Unwind, parse_panic_strategy, [TRACKED], - "panic strategy for panics in drops"), - parse_crate_root_only: bool = (false, parse_bool, [UNTRACKED], - "parse the crate root file only; do not parse other files, compile, assemble, or link \ - (default: no)"), - patchable_function_entry: PatchableFunctionEntry = (PatchableFunctionEntry::default(), parse_patchable_function_entry, [TRACKED], - "nop padding at function entry"), - plt: Option = (None, parse_opt_bool, [TRACKED], - "whether to use the PLT when calling into shared libraries; - only has effect for PIC code on systems with ELF binaries - (default: PLT is disabled if full relro is enabled on x86_64)"), - polonius: Polonius = (Polonius::default(), parse_polonius, [TRACKED], - "enable polonius-based borrow-checker (default: no)"), - pre_link_arg: (/* redirected to pre_link_args */) = ((), parse_string_push, [UNTRACKED], - "a single extra argument to prepend the linker invocation (can be used several times)"), - pre_link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], - "extra arguments to prepend to the linker invocation (space separated)"), - precise_enum_drop_elaboration: bool = (true, parse_bool, [TRACKED], - "use a more precise version of drop elaboration for matches on enums (default: yes). \ - This results in better codegen, but has caused miscompilations on some tier 2 platforms. \ - See #77382 and #74551."), - #[rustc_lint_opt_deny_field_access("use `Session::print_codegen_stats` instead of this field")] - print_codegen_stats: bool = (false, parse_bool, [UNTRACKED], - "print codegen statistics (default: no)"), - print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], - "print the LLVM optimization passes being run (default: no)"), - print_mono_items: bool = (false, parse_bool, [UNTRACKED], - "print the result of the monomorphization collection pass (default: no)"), - print_type_sizes: bool = (false, parse_bool, [UNTRACKED], - "print layout information for each type encountered (default: no)"), - proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED], - "show backtraces for panics during proc-macro execution (default: no)"), - proc_macro_execution_strategy: ProcMacroExecutionStrategy = (ProcMacroExecutionStrategy::SameThread, - parse_proc_macro_execution_strategy, [UNTRACKED], - "how to run proc-macro code (default: same-thread)"), - profile_closures: bool = (false, parse_no_value, [UNTRACKED], - "profile size of closures"), - profile_sample_use: Option = (None, parse_opt_pathbuf, [TRACKED], - "use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"), - profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED], - "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"), - query_dep_graph: bool = (false, parse_bool, [UNTRACKED], - "enable queries of the dependency graph for regression testing (default: no)"), - randomize_layout: bool = (false, parse_bool, [TRACKED], - "randomize the layout of types (default: no)"), - reg_struct_return: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], - "On x86-32 targets, it overrides the default ABI to return small structs in registers. - It is UNSOUND to link together crates that use different values for this flag!"), - regparm: Option = (None, parse_opt_number, [TRACKED TARGET_MODIFIER], - "On x86-32 targets, setting this to N causes the compiler to pass N arguments \ - in registers EAX, EDX, and ECX instead of on the stack for\ - \"C\", \"cdecl\", and \"stdcall\" fn.\ - It is UNSOUND to link together crates that use different values for this flag!"), - relax_elf_relocations: Option = (None, parse_opt_bool, [TRACKED], - "whether ELF relocations can be relaxed"), - remap_cwd_prefix: Option = (None, parse_opt_pathbuf, [TRACKED], - "remap paths under the current working directory to this path prefix"), - remark_dir: Option = (None, parse_opt_pathbuf, [UNTRACKED], - "directory into which to write optimization remarks (if not specified, they will be \ -written to standard error output)"), - retpoline: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], - "enables retpoline-indirect-branches and retpoline-indirect-calls target features (default: no)"), - retpoline_external_thunk: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], - "enables retpoline-external-thunk, retpoline-indirect-branches and retpoline-indirect-calls \ - target features (default: no)"), - #[rustc_lint_opt_deny_field_access("use `Session::sanitizers()` instead of this field")] - sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED TARGET_MODIFIER], - "use a sanitizer"), - sanitizer_cfi_canonical_jump_tables: Option = (Some(true), parse_opt_bool, [TRACKED], - "enable canonical jump tables (default: yes)"), - sanitizer_cfi_generalize_pointers: Option = (None, parse_opt_bool, [TRACKED], - "enable generalizing pointer types (default: no)"), - sanitizer_cfi_normalize_integers: Option = (None, parse_opt_bool, [TRACKED TARGET_MODIFIER], - "enable normalizing integer types (default: no)"), - sanitizer_dataflow_abilist: Vec = (Vec::new(), parse_comma_list, [TRACKED], - "additional ABI list files that control how shadow parameters are passed (comma separated)"), - sanitizer_kcfi_arity: Option = (None, parse_opt_bool, [TRACKED], - "enable KCFI arity indicator (default: no)"), - sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED], - "enable origins tracking in MemorySanitizer"), - sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], - "enable recovery for selected sanitizers"), - saturating_float_casts: Option = (None, parse_opt_bool, [TRACKED], - "make float->int casts UB-free: numbers outside the integer type's range are clipped to \ - the max/min integer respectively, and NaN is mapped to 0 (default: yes)"), - self_profile: SwitchWithOptPath = (SwitchWithOptPath::Disabled, - parse_switch_with_opt_path, [UNTRACKED], - "run the self profiler and output the raw event data"), - self_profile_counter: String = ("wall-time".to_string(), parse_string, [UNTRACKED], - "counter used by the self profiler (default: `wall-time`), one of: - `wall-time` (monotonic clock, i.e. `std::time::Instant`) - `instructions:u` (retired instructions, userspace-only) - `instructions-minus-irqs:u` (subtracting hardware interrupt counts for extra accuracy)" - ), - /// keep this in sync with the event filter names in librustc_data_structures/profiling.rs - self_profile_events: Option> = (None, parse_opt_comma_list, [UNTRACKED], - "specify the events recorded by the self profiler; - for example: `-Z self-profile-events=default,query-keys` - all options: none, all, default, generic-activity, query-provider, query-cache-hit - query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes"), - share_generics: Option = (None, parse_opt_bool, [TRACKED], - "make the current crate share its generic instantiations"), - shell_argfiles: bool = (false, parse_bool, [UNTRACKED], - "allow argument files to be specified with POSIX \"shell-style\" argument quoting"), - simulate_remapped_rust_src_base: Option = (None, parse_opt_pathbuf, [TRACKED], - "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \ - to rust's source base directory. only meant for testing purposes"), - small_data_threshold: Option = (None, parse_opt_number, [TRACKED], - "Set the threshold for objects to be stored in a \"small data\" section"), - span_debug: bool = (false, parse_bool, [UNTRACKED], - "forward proc_macro::Span's `Debug` impl to `Span`"), - /// o/w tests have closure@path - span_free_formats: bool = (false, parse_bool, [UNTRACKED], - "exclude spans when debug-printing compiler state (default: no)"), - split_dwarf_inlining: bool = (false, parse_bool, [TRACKED], - "provide minimal debug info in the object/executable to facilitate online \ - symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"), - split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [TRACKED], - "split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform) - (default: `split`) - - `split`: sections which do not require relocation are written into a DWARF object (`.dwo`) - file which is ignored by the linker - `single`: sections which do not require relocation are written into object file but ignored - by the linker"), - split_dwarf_out_dir : Option = (None, parse_opt_pathbuf, [TRACKED], - "location for writing split DWARF objects (`.dwo`) if enabled"), - split_lto_unit: Option = (None, parse_opt_bool, [TRACKED], - "enable LTO unit splitting (default: no)"), - src_hash_algorithm: Option = (None, parse_src_file_hash, [TRACKED], - "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), - #[rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field")] - stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED], - "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"), - staticlib_allow_rdylib_deps: bool = (false, parse_bool, [TRACKED], - "allow staticlibs to have rust dylib dependencies"), - staticlib_prefer_dynamic: bool = (false, parse_bool, [TRACKED], - "prefer dynamic linking to static linking for staticlibs (default: no)"), - strict_init_checks: bool = (false, parse_bool, [TRACKED], - "control if mem::uninitialized and mem::zeroed panic on more UB"), - #[rustc_lint_opt_deny_field_access("use `Session::teach` instead of this field")] - teach: bool = (false, parse_bool, [TRACKED], - "show extended diagnostic help (default: no)"), - temps_dir: Option = (None, parse_opt_string, [UNTRACKED], - "the directory the intermediate files are written to"), - terminal_urls: TerminalUrl = (TerminalUrl::No, parse_terminal_url, [UNTRACKED], - "use the OSC 8 hyperlink terminal specification to print hyperlinks in the compiler output"), - #[rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field")] - thinlto: Option = (None, parse_opt_bool, [TRACKED], - "enable ThinLTO when possible"), - /// We default to 1 here since we want to behave like - /// a sequential compiler for now. This'll likely be adjusted - /// in the future. Note that -Zthreads=0 is the way to get - /// the num_cpus behavior. - #[rustc_lint_opt_deny_field_access("use `Session::threads` instead of this field")] - threads: usize = (1, parse_threads, [UNTRACKED], - "use a thread pool with N threads"), - time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], - "measure time of each LLVM pass (default: no)"), - time_passes: bool = (false, parse_bool, [UNTRACKED], - "measure time of each rustc pass (default: no)"), - time_passes_format: TimePassesFormat = (TimePassesFormat::Text, parse_time_passes_format, [UNTRACKED], - "the format to use for -Z time-passes (`text` (default) or `json`)"), - tiny_const_eval_limit: bool = (false, parse_bool, [TRACKED], - "sets a tiny, non-configurable limit for const eval; useful for compiler tests"), - #[rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field")] - tls_model: Option = (None, parse_tls_model, [TRACKED], - "choose the TLS model to use (`rustc --print tls-models` for details)"), - trace_macros: bool = (false, parse_bool, [UNTRACKED], - "for every macro invocation, print its name and arguments (default: no)"), - track_diagnostics: bool = (false, parse_bool, [UNTRACKED], - "tracks where in rustc a diagnostic was emitted"), - translate_remapped_path_to_local_path: bool = (true, parse_bool, [TRACKED], - "translate remapped paths into local paths when possible (default: yes)"), - trap_unreachable: Option = (None, parse_opt_bool, [TRACKED], - "generate trap instructions for unreachable intrinsics (default: use target setting, usually yes)"), - treat_err_as_bug: Option> = (None, parse_treat_err_as_bug, [TRACKED], - "treat the `val`th error that occurs as bug (default if not specified: 0 - don't treat errors as bugs. \ - default if specified without a value: 1 - treat the first error as bug)"), - trim_diagnostic_paths: bool = (true, parse_bool, [UNTRACKED], - "in diagnostics, use heuristics to shorten paths referring to items"), - tune_cpu: Option = (None, parse_opt_string, [TRACKED], - "select processor to schedule for (`rustc --print target-cpus` for details)"), - #[rustc_lint_opt_deny_field_access("use `TyCtxt::use_typing_mode_borrowck` instead of this field")] - typing_mode_borrowck: bool = (false, parse_bool, [TRACKED], - "enable `TypingMode::Borrowck`, changing the way opaque types are handled during MIR borrowck"), - #[rustc_lint_opt_deny_field_access("use `Session::ub_checks` instead of this field")] - ub_checks: Option = (None, parse_opt_bool, [TRACKED], - "emit runtime checks for Undefined Behavior (default: -Cdebug-assertions)"), - ui_testing: bool = (false, parse_bool, [UNTRACKED], - "emit compiler diagnostics in a form suitable for UI testing (default: no)"), - uninit_const_chunk_threshold: usize = (16, parse_number, [TRACKED], - "allow generating const initializers with mixed init/uninit chunks, \ - and set the maximum number of chunks for which this is allowed (default: 16)"), - unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], - "take the brakes off const evaluation. NOTE: this is unsound (default: no)"), - unpretty: Option = (None, parse_unpretty, [UNTRACKED], - "present the input source, unstable (and less-pretty) variants; - `normal`, `identified`, - `expanded`, `expanded,identified`, - `expanded,hygiene` (with internal representations), - `ast-tree` (raw AST before expansion), - `ast-tree,expanded` (raw AST after expansion), - `hir` (the HIR), `hir,identified`, - `hir,typed` (HIR with types for each node), - `hir-tree` (dump the raw HIR), - `thir-tree`, `thir-flat`, - `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR)"), - unsound_mir_opts: bool = (false, parse_bool, [TRACKED], - "enable unsound and buggy MIR optimizations (default: no)"), - /// This name is kind of confusing: Most unstable options enable something themselves, while - /// this just allows "normal" options to be feature-gated. - /// - /// The main check for `-Zunstable-options` takes place separately from the - /// usual parsing of `-Z` options (see [`crate::config::nightly_options`]), - /// so this boolean value is mostly used for enabling unstable _values_ of - /// stable options. That separate check doesn't handle boolean values, so - /// to avoid an inconsistent state we also forbid them here. - #[rustc_lint_opt_deny_field_access("use `Session::unstable_options` instead of this field")] - unstable_options: bool = (false, parse_no_value, [UNTRACKED], - "adds unstable command line options to rustc interface (default: no)"), - use_ctors_section: Option = (None, parse_opt_bool, [TRACKED], - "use legacy .ctors section for initializers rather than .init_array"), - use_sync_unwind: Option = (None, parse_opt_bool, [TRACKED], - "Generate sync unwind tables instead of async unwind tables (default: no)"), - validate_mir: bool = (false, parse_bool, [UNTRACKED], - "validate MIR after each transformation"), - verbose_asm: bool = (false, parse_bool, [TRACKED], - "add descriptive comments from LLVM to the assembly (may change behavior) (default: no)"), - #[rustc_lint_opt_deny_field_access("use `Session::verbose_internals` instead of this field")] - verbose_internals: bool = (false, parse_bool, [TRACKED_NO_CRATE_HASH], - "in general, enable more debug printouts (default: no)"), - #[rustc_lint_opt_deny_field_access("use `Session::verify_llvm_ir` instead of this field")] - verify_llvm_ir: bool = (false, parse_bool, [TRACKED], - "verify LLVM IR (default: no)"), - virtual_function_elimination: bool = (false, parse_bool, [TRACKED], - "enables dead virtual function elimination optimization. \ - Requires `-Clto[=[fat,yes]]`"), - wasi_exec_model: Option = (None, parse_wasi_exec_model, [TRACKED], - "whether to build a wasi command or reactor"), - // This option only still exists to provide a more gradual transition path for people who need - // the spec-complaint C ABI to be used. - // FIXME remove this after a couple releases - wasm_c_abi: () = ((), parse_wasm_c_abi, [TRACKED], - "use spec-compliant C ABI for `wasm32-unknown-unknown` (deprecated, always enabled)"), - write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED], - "whether long type names should be written to files instead of being printed in errors"), - // tidy-alphabetical-end - - // If you add a new option, please update: - // - compiler/rustc_interface/src/tests.rs - // - src/doc/unstable-book/src/compiler-flags -} +include!("options/unstable.rs"); diff --git a/compiler/rustc_session/src/options/unstable.rs b/compiler/rustc_session/src/options/unstable.rs new file mode 100644 index 0000000000000..dc00bd8a223af --- /dev/null +++ b/compiler/rustc_session/src/options/unstable.rs @@ -0,0 +1,585 @@ +options! { + UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, "Z", "unstable", + + // If you add a new option, please update: + // - compiler/rustc_interface/src/tests.rs + // - src/doc/unstable-book/src/compiler-flags + + // tidy-alphabetical-start + allow_features: Option> = (None, parse_opt_comma_list, [TRACKED], + "only allow the listed language features to be enabled in code (comma separated)"), + always_encode_mir: bool = (false, parse_bool, [TRACKED], + "encode MIR of all functions into the crate metadata (default: no)"), + annotate_moves: AnnotateMoves = (AnnotateMoves::Disabled, parse_annotate_moves, [TRACKED], + "emit debug info for compiler-generated move and copy operations \ + to make them visible in profilers. Can be a boolean or a size limit in bytes (default: disabled)"), + assert_incr_state: Option = (None, parse_opt_string, [UNTRACKED], + "assert that the incremental cache is in given state: \ + either `loaded` or `not-loaded`."), + assume_incomplete_release: bool = (false, parse_bool, [TRACKED], + "make cfg(version) treat the current version as incomplete (default: no)"), + autodiff: Vec = (Vec::new(), parse_autodiff, [TRACKED], + "a list of autodiff flags to enable + Mandatory setting: + `=Enable` + Optional extra settings: + `=PrintTA` + `=PrintAA` + `=PrintPerf` + `=PrintSteps` + `=PrintModBefore` + `=PrintModAfter` + `=PrintModFinal` + `=PrintPasses`, + `=NoPostopt` + `=LooseTypes` + `=Inline` + Multiple options can be combined with commas."), + #[rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field")] + binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], + "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ + (default: no)"), + box_noalias: bool = (true, parse_bool, [TRACKED], + "emit noalias metadata for box (default: yes)"), + branch_protection: Option = (None, parse_branch_protection, [TRACKED TARGET_MODIFIER], + "set options for branch target identification and pointer authentication on AArch64"), + build_sdylib_interface: bool = (false, parse_bool, [UNTRACKED], + "whether the stable interface is being built"), + cache_proc_macros: bool = (false, parse_bool, [TRACKED], + "cache the results of derive proc macro invocations (potentially unsound!) (default: no"), + cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED], + "instrument control-flow architecture protection"), + check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED], + "show all expected values in check-cfg diagnostics (default: no)"), + checksum_hash_algorithm: Option = (None, parse_cargo_src_file_hash, [TRACKED], + "hash algorithm of source files used to check freshness in cargo (`blake3` or `sha256`)"), + codegen_backend: Option = (None, parse_opt_string, [TRACKED], + "the backend to use"), + codegen_source_order: bool = (false, parse_bool, [UNTRACKED], + "emit mono items in the order of spans in source files (default: no)"), + contract_checks: Option = (None, parse_opt_bool, [TRACKED], + "emit runtime checks for contract pre- and post-conditions (default: no)"), + coverage_options: CoverageOptions = (CoverageOptions::default(), parse_coverage_options, [TRACKED], + "control details of coverage instrumentation"), + crate_attr: Vec = (Vec::new(), parse_string_push, [TRACKED], + "inject the given attribute in the crate"), + cross_crate_inline_threshold: InliningThreshold = (InliningThreshold::Sometimes(100), parse_inlining_threshold, [TRACKED], + "threshold to allow cross crate inlining of functions"), + debug_info_for_profiling: bool = (false, parse_bool, [TRACKED], + "emit discriminators and other data necessary for AutoFDO"), + 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], + "compress debug info sections (none, zlib, zstd, default: none)"), + deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED], + "deduplicate identical diagnostics (default: yes)"), + default_visibility: Option = (None, parse_opt_symbol_visibility, [TRACKED], + "overrides the `default_visibility` setting of the target"), + dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED], + "in dep-info output, omit targets for tracking dependencies of the dep-info files \ + themselves (default: no)"), + direct_access_external_data: Option = (None, parse_opt_bool, [TRACKED], + "Direct or use GOT indirect to reference external data symbols"), + dual_proc_macros: bool = (false, parse_bool, [TRACKED], + "load proc macros for both target and host, but only link to the target (default: no)"), + dump_dep_graph: bool = (false, parse_bool, [UNTRACKED], + "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) \ + (default: no)"), + dump_mir: Option = (None, parse_opt_string, [UNTRACKED], + "dump MIR state to file. + `val` is used to select which passes and functions to dump. For example: + `all` matches all passes and functions, + `foo` matches all passes for functions whose name contains 'foo', + `foo & ConstProp` only the 'ConstProp' pass for function names containing 'foo', + `foo | bar` all passes for function names containing 'foo' or 'bar'."), + dump_mir_dataflow: bool = (false, parse_bool, [UNTRACKED], + "in addition to `.mir` files, create graphviz `.dot` files with dataflow results \ + (default: no)"), + dump_mir_dir: String = ("mir_dump".to_string(), parse_string, [UNTRACKED], + "the directory the MIR is dumped into (default: `mir_dump`)"), + dump_mir_exclude_alloc_bytes: bool = (false, parse_bool, [UNTRACKED], + "exclude the raw bytes of allocations when dumping MIR (used in tests) (default: no)"), + dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED], + "exclude the pass number when dumping MIR (used in tests) (default: no)"), + dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], + "in addition to `.mir` files, create graphviz `.dot` files (default: no)"), + dump_mono_stats: SwitchWithOptPath = (SwitchWithOptPath::Disabled, + parse_switch_with_opt_path, [UNTRACKED], + "output statistics about monomorphization collection"), + dump_mono_stats_format: DumpMonoStatsFormat = (DumpMonoStatsFormat::Markdown, parse_dump_mono_stats, [UNTRACKED], + "the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)"), + #[rustc_lint_opt_deny_field_access("use `Session::dwarf_version` instead of this field")] + dwarf_version: Option = (None, parse_opt_number, [TRACKED], + "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), + dylib_lto: bool = (false, parse_bool, [UNTRACKED], + "enables LTO for dylib crate type"), + eagerly_emit_delayed_bugs: bool = (false, parse_bool, [UNTRACKED], + "emit delayed bugs eagerly as errors instead of stashing them and emitting \ + them only if an error has not been emitted"), + ehcont_guard: bool = (false, parse_bool, [TRACKED], + "generate Windows EHCont Guard tables"), + embed_metadata: bool = (true, parse_bool, [TRACKED], + "embed metadata in rlibs and dylibs (default: yes)"), + embed_source: bool = (false, parse_bool, [TRACKED], + "embed source text in DWARF debug sections (default: no)"), + emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], + "emit a section containing stack size metadata (default: no)"), + emscripten_wasm_eh: bool = (true, parse_bool, [TRACKED], + "Use WebAssembly error handling for wasm32-unknown-emscripten"), + enforce_type_length_limit: bool = (false, parse_bool, [TRACKED], + "enforce the type length limit when monomorphizing instances in codegen"), + experimental_default_bounds: bool = (false, parse_bool, [TRACKED], + "enable default bounds for experimental group of auto traits"), + export_executable_symbols: bool = (false, parse_bool, [TRACKED], + "export symbols from executables, as if they were dynamic libraries"), + external_clangrt: bool = (false, parse_bool, [UNTRACKED], + "rely on user specified linker commands to find clangrt"), + extra_const_ub_checks: bool = (false, parse_bool, [TRACKED], + "turns on more checks to detect const UB, which can be slow (default: no)"), + #[rustc_lint_opt_deny_field_access("use `Session::fewer_names` instead of this field")] + fewer_names: Option = (None, parse_opt_bool, [TRACKED], + "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ + (default: no)"), + fixed_x18: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], + "make the x18 register reserved on AArch64 (default: no)"), + 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], + "how detailed `#[derive(Debug)]` should be. `full` prints types recursively, \ + `shallow` prints only type names, `none` prints nothing and disables `{:?}`. (default: `full`)"), + 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], + "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"), + future_incompat_test: bool = (false, parse_bool, [UNTRACKED], + "forces all lints to be future incompatible, used for internal testing (default: no)"), + graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED], + "use dark-themed colors in graphviz output (default: no)"), + graphviz_font: String = ("Courier, monospace".to_string(), parse_string, [UNTRACKED], + "use the given `fontname` in graphviz output; can be overridden by setting \ + environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"), + has_thread_local: Option = (None, parse_opt_bool, [TRACKED], + "explicitly enable the `cfg(target_thread_local)` directive"), + help: bool = (false, parse_no_value, [UNTRACKED], "Print unstable compiler options"), + higher_ranked_assumptions: bool = (false, parse_bool, [TRACKED], + "allow deducing higher-ranked outlives assumptions from coroutines when proving auto traits"), + hint_mostly_unused: bool = (false, parse_bool, [TRACKED], + "hint that most of this crate will go unused, to minimize work for uncalled functions"), + human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], + "generate human-readable, predictable names for codegen units (default: no)"), + identify_regions: bool = (false, parse_bool, [UNTRACKED], + "display unnamed regions as `'`, using a non-ident unique id (default: no)"), + ignore_directory_in_diagnostics_source_blocks: Vec = (Vec::new(), parse_string_push, [UNTRACKED], + "do not display the source code block in diagnostics for files in the directory"), + incremental_ignore_spans: bool = (false, parse_bool, [TRACKED], + "ignore spans during ICH computation -- used for testing (default: no)"), + incremental_info: bool = (false, parse_bool, [UNTRACKED], + "print high-level information about incremental reuse (or the lack thereof) \ + (default: no)"), + incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED], + "verify extended properties for incr. comp. (default: no): + - hashes of green query instances + - hash collisions of query keys + - hash collisions when creating dep-nodes"), + indirect_branch_cs_prefix: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], + "add `cs` prefix to `call` and `jmp` to indirect thunks (default: no)"), + inline_llvm: bool = (true, parse_bool, [TRACKED], + "enable LLVM inlining (default: yes)"), + inline_mir: Option = (None, parse_opt_bool, [TRACKED], + "enable MIR inlining (default: no)"), + inline_mir_forwarder_threshold: Option = (None, parse_opt_number, [TRACKED], + "inlining threshold when the caller is a simple forwarding function (default: 30)"), + inline_mir_hint_threshold: Option = (None, parse_opt_number, [TRACKED], + "inlining threshold for functions with inline hint (default: 100)"), + inline_mir_preserve_debug: Option = (None, parse_opt_bool, [TRACKED], + "when MIR inlining, whether to preserve debug info for callee variables \ + (default: preserve for debuginfo != None, otherwise remove)"), + inline_mir_threshold: Option = (None, parse_opt_number, [TRACKED], + "a default MIR inlining threshold (default: 50)"), + input_stats: bool = (false, parse_bool, [UNTRACKED], + "print some statistics about AST and HIR (default: no)"), + instrument_mcount: bool = (false, parse_bool, [TRACKED], + "insert function instrument code for mcount-based tracing (default: no)"), + instrument_xray: Option = (None, parse_instrument_xray, [TRACKED], + "insert function instrument code for XRay-based tracing (default: no) + Optional extra settings: + `=always` + `=never` + `=ignore-loops` + `=instruction-threshold=N` + `=skip-entry` + `=skip-exit` + Multiple options can be combined with commas."), + large_data_threshold: Option = (None, parse_opt_number, [TRACKED], + "set the threshold for objects to be stored in a \"large data\" section \ + (only effective with -Ccode-model=medium, default: 65536)"), + layout_seed: Option = (None, parse_opt_number, [TRACKED], + "seed layout randomization"), + link_directives: bool = (true, parse_bool, [TRACKED], + "honor #[link] directives in the compiled crate (default: yes)"), + link_native_libraries: bool = (true, parse_bool, [UNTRACKED], + "link native libraries in the linker invocation (default: yes)"), + link_only: bool = (false, parse_bool, [TRACKED], + "link the `.rlink` file generated by `-Z no-link` (default: no)"), + lint_llvm_ir: bool = (false, parse_bool, [TRACKED], + "lint LLVM IR (default: no)"), + lint_mir: bool = (false, parse_bool, [UNTRACKED], + "lint MIR before and after each transformation"), + llvm_module_flag: Vec<(String, u32, String)> = (Vec::new(), parse_llvm_module_flag, [TRACKED], + "a list of module flags to pass to LLVM (space separated)"), + llvm_plugins: Vec = (Vec::new(), parse_list, [TRACKED], + "a list LLVM plugins to enable (space separated)"), + llvm_time_trace: bool = (false, parse_bool, [UNTRACKED], + "generate JSON tracing data file from LLVM data (default: no)"), + location_detail: LocationDetail = (LocationDetail::all(), parse_location_detail, [TRACKED], + "what location details should be tracked when using caller_location, either \ + `none`, or a comma separated list of location details, for which \ + valid options are `file`, `line`, and `column` (default: `file,line,column`)"), + ls: Vec = (Vec::new(), parse_list, [UNTRACKED], + "decode and print various parts of the crate metadata for a library crate \ + (space separated)"), + macro_backtrace: bool = (false, parse_bool, [UNTRACKED], + "show macro backtraces (default: no)"), + macro_stats: bool = (false, parse_bool, [UNTRACKED], + "print some statistics about macro expansions (default: no)"), + maximal_hir_to_mir_coverage: bool = (false, parse_bool, [TRACKED], + "save as much information as possible about the correspondence between MIR and HIR \ + as source scopes (default: no)"), + merge_functions: Option = (None, parse_merge_functions, [TRACKED], + "control the operation of the MergeFunctions LLVM pass, taking \ + the same values as the target option of the same name"), + meta_stats: bool = (false, parse_bool, [UNTRACKED], + "gather metadata statistics (default: no)"), + metrics_dir: Option = (None, parse_opt_pathbuf, [UNTRACKED], + "the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"), + min_function_alignment: Option = (None, parse_align, [TRACKED], + "align all functions to at least this many bytes. Must be a power of 2"), + min_recursion_limit: Option = (None, parse_opt_number, [TRACKED], + "set a minimum recursion limit (final limit = max(this, recursion_limit_from_crate))"), + mir_emit_retag: bool = (false, parse_bool, [TRACKED], + "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ + (default: no)"), + mir_enable_passes: Vec<(String, bool)> = (Vec::new(), parse_list_with_polarity, [TRACKED], + "use like `-Zmir-enable-passes=+DestinationPropagation,-InstSimplify`. Forces the \ + specified passes to be enabled, overriding all other checks. In particular, this will \ + enable unsound (known-buggy and hence usually disabled) passes without further warning! \ + Passes that are not specified are enabled or disabled by other flags as usual."), + mir_include_spans: MirIncludeSpans = (MirIncludeSpans::default(), parse_mir_include_spans, [UNTRACKED], + "include extra comments in mir pretty printing, like line numbers and statement indices, \ + details about types, etc. (boolean for all passes, 'nll' to enable in NLL MIR only, default: 'nll')"), + mir_opt_bisect_limit: Option = (None, parse_opt_number, [TRACKED], + "limit the number of MIR optimization pass executions (global across all bodies). \ + Pass executions after this limit are skipped and reported. (default: no limit)"), + #[rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field")] + mir_opt_level: Option = (None, parse_opt_number, [TRACKED], + "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), + 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], + "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"), + mutable_noalias: bool = (true, parse_bool, [TRACKED], + "emit noalias metadata for mutable references (default: yes)"), + namespaced_crates: bool = (false, parse_bool, [TRACKED], + "allow crates to be namespaced by other crates (default: no)"), + next_solver: NextSolverConfig = (NextSolverConfig::default(), parse_next_solver_config, [TRACKED], + "enable and configure the next generation trait solver used by rustc"), + nll_facts: bool = (false, parse_bool, [UNTRACKED], + "dump facts from NLL analysis into side files (default: no)"), + nll_facts_dir: String = ("nll-facts".to_string(), parse_string, [UNTRACKED], + "the directory the NLL facts are dumped into (default: `nll-facts`)"), + no_analysis: bool = (false, parse_no_value, [UNTRACKED], + "parse and expand the source, but run no analysis"), + no_codegen: bool = (false, parse_no_value, [TRACKED_NO_CRATE_HASH], + "run all passes except codegen; no output"), + no_generate_arange_section: bool = (false, parse_no_value, [TRACKED], + "omit DWARF address ranges that give faster lookups"), + no_implied_bounds_compat: bool = (false, parse_bool, [TRACKED], + "disable the compatibility version of the `implied_bounds_ty` query"), + no_leak_check: bool = (false, parse_no_value, [UNTRACKED], + "disable the 'leak check' for subtyping; unsound, but useful for tests"), + no_link: bool = (false, parse_no_value, [TRACKED], + "compile without linking"), + no_parallel_backend: bool = (false, parse_no_value, [UNTRACKED], + "run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"), + no_profiler_runtime: bool = (false, parse_no_value, [TRACKED], + "prevent automatic injection of the profiler_builtins crate"), + no_steal_thir: bool = (false, parse_bool, [UNTRACKED], + "don't steal the THIR when we're done with it; useful for rustc drivers (default: no)"), + no_trait_vptr: bool = (false, parse_no_value, [TRACKED], + "disable generation of trait vptr in vtable for upcasting"), + no_unique_section_names: bool = (false, parse_bool, [TRACKED], + "do not use unique names for text and data sections when -Z function-sections is used"), + normalize_docs: bool = (false, parse_bool, [TRACKED], + "normalize associated items in rustdoc when generating documentation"), + offload: Vec = (Vec::new(), parse_offload, [TRACKED], + "a list of offload flags to enable + Mandatory setting: + `=Enable` + Currently the only option available"), + on_broken_pipe: OnBrokenPipe = (OnBrokenPipe::Default, parse_on_broken_pipe, [TRACKED], + "behavior of std::io::ErrorKind::BrokenPipe (SIGPIPE)"), + osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], + "pass `-install_name @rpath/...` to the macOS linker (default: no)"), + packed_bundled_libs: bool = (false, parse_bool, [TRACKED], + "change rlib format to store native libraries as archives"), + panic_abort_tests: bool = (false, parse_bool, [TRACKED], + "support compiling tests with panic=abort (default: no)"), + panic_in_drop: PanicStrategy = (PanicStrategy::Unwind, parse_panic_strategy, [TRACKED], + "panic strategy for panics in drops"), + parse_crate_root_only: bool = (false, parse_bool, [UNTRACKED], + "parse the crate root file only; do not parse other files, compile, assemble, or link \ + (default: no)"), + patchable_function_entry: PatchableFunctionEntry = (PatchableFunctionEntry::default(), parse_patchable_function_entry, [TRACKED], + "nop padding at function entry"), + plt: Option = (None, parse_opt_bool, [TRACKED], + "whether to use the PLT when calling into shared libraries; + only has effect for PIC code on systems with ELF binaries + (default: PLT is disabled if full relro is enabled on x86_64)"), + polonius: Polonius = (Polonius::default(), parse_polonius, [TRACKED], + "enable polonius-based borrow-checker (default: no)"), + pre_link_arg: (/* redirected to pre_link_args */) = ((), parse_string_push, [UNTRACKED], + "a single extra argument to prepend the linker invocation (can be used several times)"), + pre_link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], + "extra arguments to prepend to the linker invocation (space separated)"), + precise_enum_drop_elaboration: bool = (true, parse_bool, [TRACKED], + "use a more precise version of drop elaboration for matches on enums (default: yes). \ + This results in better codegen, but has caused miscompilations on some tier 2 platforms. \ + See #77382 and #74551."), + #[rustc_lint_opt_deny_field_access("use `Session::print_codegen_stats` instead of this field")] + print_codegen_stats: bool = (false, parse_bool, [UNTRACKED], + "print codegen statistics (default: no)"), + print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], + "print the LLVM optimization passes being run (default: no)"), + print_mono_items: bool = (false, parse_bool, [UNTRACKED], + "print the result of the monomorphization collection pass (default: no)"), + print_type_sizes: bool = (false, parse_bool, [UNTRACKED], + "print layout information for each type encountered (default: no)"), + proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED], + "show backtraces for panics during proc-macro execution (default: no)"), + proc_macro_execution_strategy: ProcMacroExecutionStrategy = (ProcMacroExecutionStrategy::SameThread, + parse_proc_macro_execution_strategy, [UNTRACKED], + "how to run proc-macro code (default: same-thread)"), + profile_closures: bool = (false, parse_no_value, [UNTRACKED], + "profile size of closures"), + profile_sample_use: Option = (None, parse_opt_pathbuf, [TRACKED], + "use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"), + profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED], + "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"), + query_dep_graph: bool = (false, parse_bool, [UNTRACKED], + "enable queries of the dependency graph for regression testing (default: no)"), + randomize_layout: bool = (false, parse_bool, [TRACKED], + "randomize the layout of types (default: no)"), + reg_struct_return: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], + "On x86-32 targets, it overrides the default ABI to return small structs in registers. + It is UNSOUND to link together crates that use different values for this flag!"), + regparm: Option = (None, parse_opt_number, [TRACKED TARGET_MODIFIER], + "On x86-32 targets, setting this to N causes the compiler to pass N arguments \ + in registers EAX, EDX, and ECX instead of on the stack for\ + \"C\", \"cdecl\", and \"stdcall\" fn.\ + It is UNSOUND to link together crates that use different values for this flag!"), + relax_elf_relocations: Option = (None, parse_opt_bool, [TRACKED], + "whether ELF relocations can be relaxed"), + remap_cwd_prefix: Option = (None, parse_opt_pathbuf, [TRACKED], + "remap paths under the current working directory to this path prefix"), + remark_dir: Option = (None, parse_opt_pathbuf, [UNTRACKED], + "directory into which to write optimization remarks (if not specified, they will be \ +written to standard error output)"), + retpoline: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], + "enables retpoline-indirect-branches and retpoline-indirect-calls target features (default: no)"), + retpoline_external_thunk: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], + "enables retpoline-external-thunk, retpoline-indirect-branches and retpoline-indirect-calls \ + target features (default: no)"), + #[rustc_lint_opt_deny_field_access("use `Session::sanitizers()` instead of this field")] + sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED TARGET_MODIFIER], + "use a sanitizer"), + sanitizer_cfi_canonical_jump_tables: Option = (Some(true), parse_opt_bool, [TRACKED], + "enable canonical jump tables (default: yes)"), + sanitizer_cfi_generalize_pointers: Option = (None, parse_opt_bool, [TRACKED], + "enable generalizing pointer types (default: no)"), + sanitizer_cfi_normalize_integers: Option = (None, parse_opt_bool, [TRACKED TARGET_MODIFIER], + "enable normalizing integer types (default: no)"), + sanitizer_dataflow_abilist: Vec = (Vec::new(), parse_comma_list, [TRACKED], + "additional ABI list files that control how shadow parameters are passed (comma separated)"), + sanitizer_kcfi_arity: Option = (None, parse_opt_bool, [TRACKED], + "enable KCFI arity indicator (default: no)"), + sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED], + "enable origins tracking in MemorySanitizer"), + sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], + "enable recovery for selected sanitizers"), + saturating_float_casts: Option = (None, parse_opt_bool, [TRACKED], + "make float->int casts UB-free: numbers outside the integer type's range are clipped to \ + the max/min integer respectively, and NaN is mapped to 0 (default: yes)"), + self_profile: SwitchWithOptPath = (SwitchWithOptPath::Disabled, + parse_switch_with_opt_path, [UNTRACKED], + "run the self profiler and output the raw event data"), + self_profile_counter: String = ("wall-time".to_string(), parse_string, [UNTRACKED], + "counter used by the self profiler (default: `wall-time`), one of: + `wall-time` (monotonic clock, i.e. `std::time::Instant`) + `instructions:u` (retired instructions, userspace-only) + `instructions-minus-irqs:u` (subtracting hardware interrupt counts for extra accuracy)" + ), + /// keep this in sync with the event filter names in librustc_data_structures/profiling.rs + self_profile_events: Option> = (None, parse_opt_comma_list, [UNTRACKED], + "specify the events recorded by the self profiler; + for example: `-Z self-profile-events=default,query-keys` + all options: none, all, default, generic-activity, query-provider, query-cache-hit + query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes"), + share_generics: Option = (None, parse_opt_bool, [TRACKED], + "make the current crate share its generic instantiations"), + shell_argfiles: bool = (false, parse_bool, [UNTRACKED], + "allow argument files to be specified with POSIX \"shell-style\" argument quoting"), + simulate_remapped_rust_src_base: Option = (None, parse_opt_pathbuf, [TRACKED], + "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \ + to rust's source base directory. only meant for testing purposes"), + small_data_threshold: Option = (None, parse_opt_number, [TRACKED], + "Set the threshold for objects to be stored in a \"small data\" section"), + span_debug: bool = (false, parse_bool, [UNTRACKED], + "forward proc_macro::Span's `Debug` impl to `Span`"), + /// o/w tests have closure@path + span_free_formats: bool = (false, parse_bool, [UNTRACKED], + "exclude spans when debug-printing compiler state (default: no)"), + split_dwarf_inlining: bool = (false, parse_bool, [TRACKED], + "provide minimal debug info in the object/executable to facilitate online \ + symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"), + split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [TRACKED], + "split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform) + (default: `split`) + + `split`: sections which do not require relocation are written into a DWARF object (`.dwo`) + file which is ignored by the linker + `single`: sections which do not require relocation are written into object file but ignored + by the linker"), + split_dwarf_out_dir : Option = (None, parse_opt_pathbuf, [TRACKED], + "location for writing split DWARF objects (`.dwo`) if enabled"), + split_lto_unit: Option = (None, parse_opt_bool, [TRACKED], + "enable LTO unit splitting (default: no)"), + src_hash_algorithm: Option = (None, parse_src_file_hash, [TRACKED], + "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), + #[rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field")] + stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED], + "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"), + staticlib_allow_rdylib_deps: bool = (false, parse_bool, [TRACKED], + "allow staticlibs to have rust dylib dependencies"), + staticlib_prefer_dynamic: bool = (false, parse_bool, [TRACKED], + "prefer dynamic linking to static linking for staticlibs (default: no)"), + strict_init_checks: bool = (false, parse_bool, [TRACKED], + "control if mem::uninitialized and mem::zeroed panic on more UB"), + #[rustc_lint_opt_deny_field_access("use `Session::teach` instead of this field")] + teach: bool = (false, parse_bool, [TRACKED], + "show extended diagnostic help (default: no)"), + temps_dir: Option = (None, parse_opt_string, [UNTRACKED], + "the directory the intermediate files are written to"), + terminal_urls: TerminalUrl = (TerminalUrl::No, parse_terminal_url, [UNTRACKED], + "use the OSC 8 hyperlink terminal specification to print hyperlinks in the compiler output"), + #[rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field")] + thinlto: Option = (None, parse_opt_bool, [TRACKED], + "enable ThinLTO when possible"), + /// We default to 1 here since we want to behave like + /// a sequential compiler for now. This'll likely be adjusted + /// in the future. Note that -Zthreads=0 is the way to get + /// the num_cpus behavior. + #[rustc_lint_opt_deny_field_access("use `Session::threads` instead of this field")] + threads: usize = (1, parse_threads, [UNTRACKED], + "use a thread pool with N threads"), + time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], + "measure time of each LLVM pass (default: no)"), + time_passes: bool = (false, parse_bool, [UNTRACKED], + "measure time of each rustc pass (default: no)"), + time_passes_format: TimePassesFormat = (TimePassesFormat::Text, parse_time_passes_format, [UNTRACKED], + "the format to use for -Z time-passes (`text` (default) or `json`)"), + tiny_const_eval_limit: bool = (false, parse_bool, [TRACKED], + "sets a tiny, non-configurable limit for const eval; useful for compiler tests"), + #[rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field")] + tls_model: Option = (None, parse_tls_model, [TRACKED], + "choose the TLS model to use (`rustc --print tls-models` for details)"), + trace_macros: bool = (false, parse_bool, [UNTRACKED], + "for every macro invocation, print its name and arguments (default: no)"), + track_diagnostics: bool = (false, parse_bool, [UNTRACKED], + "tracks where in rustc a diagnostic was emitted"), + translate_remapped_path_to_local_path: bool = (true, parse_bool, [TRACKED], + "translate remapped paths into local paths when possible (default: yes)"), + trap_unreachable: Option = (None, parse_opt_bool, [TRACKED], + "generate trap instructions for unreachable intrinsics (default: use target setting, usually yes)"), + treat_err_as_bug: Option> = (None, parse_treat_err_as_bug, [TRACKED], + "treat the `val`th error that occurs as bug (default if not specified: 0 - don't treat errors as bugs. \ + default if specified without a value: 1 - treat the first error as bug)"), + trim_diagnostic_paths: bool = (true, parse_bool, [UNTRACKED], + "in diagnostics, use heuristics to shorten paths referring to items"), + tune_cpu: Option = (None, parse_opt_string, [TRACKED], + "select processor to schedule for (`rustc --print target-cpus` for details)"), + #[rustc_lint_opt_deny_field_access("use `TyCtxt::use_typing_mode_borrowck` instead of this field")] + typing_mode_borrowck: bool = (false, parse_bool, [TRACKED], + "enable `TypingMode::Borrowck`, changing the way opaque types are handled during MIR borrowck"), + #[rustc_lint_opt_deny_field_access("use `Session::ub_checks` instead of this field")] + ub_checks: Option = (None, parse_opt_bool, [TRACKED], + "emit runtime checks for Undefined Behavior (default: -Cdebug-assertions)"), + ui_testing: bool = (false, parse_bool, [UNTRACKED], + "emit compiler diagnostics in a form suitable for UI testing (default: no)"), + uninit_const_chunk_threshold: usize = (16, parse_number, [TRACKED], + "allow generating const initializers with mixed init/uninit chunks, \ + and set the maximum number of chunks for which this is allowed (default: 16)"), + unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], + "take the brakes off const evaluation. NOTE: this is unsound (default: no)"), + unpretty: Option = (None, parse_unpretty, [UNTRACKED], + "present the input source, unstable (and less-pretty) variants; + `normal`, `identified`, + `expanded`, `expanded,identified`, + `expanded,hygiene` (with internal representations), + `ast-tree` (raw AST before expansion), + `ast-tree,expanded` (raw AST after expansion), + `hir` (the HIR), `hir,identified`, + `hir,typed` (HIR with types for each node), + `hir-tree` (dump the raw HIR), + `thir-tree`, `thir-flat`, + `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR)"), + unsound_mir_opts: bool = (false, parse_bool, [TRACKED], + "enable unsound and buggy MIR optimizations (default: no)"), + /// This name is kind of confusing: Most unstable options enable something themselves, while + /// this just allows "normal" options to be feature-gated. + /// + /// The main check for `-Zunstable-options` takes place separately from the + /// usual parsing of `-Z` options (see [`crate::config::nightly_options`]), + /// so this boolean value is mostly used for enabling unstable _values_ of + /// stable options. That separate check doesn't handle boolean values, so + /// to avoid an inconsistent state we also forbid them here. + #[rustc_lint_opt_deny_field_access("use `Session::unstable_options` instead of this field")] + unstable_options: bool = (false, parse_no_value, [UNTRACKED], + "adds unstable command line options to rustc interface (default: no)"), + use_ctors_section: Option = (None, parse_opt_bool, [TRACKED], + "use legacy .ctors section for initializers rather than .init_array"), + use_sync_unwind: Option = (None, parse_opt_bool, [TRACKED], + "Generate sync unwind tables instead of async unwind tables (default: no)"), + validate_mir: bool = (false, parse_bool, [UNTRACKED], + "validate MIR after each transformation"), + verbose_asm: bool = (false, parse_bool, [TRACKED], + "add descriptive comments from LLVM to the assembly (may change behavior) (default: no)"), + #[rustc_lint_opt_deny_field_access("use `Session::verbose_internals` instead of this field")] + verbose_internals: bool = (false, parse_bool, [TRACKED_NO_CRATE_HASH], + "in general, enable more debug printouts (default: no)"), + #[rustc_lint_opt_deny_field_access("use `Session::verify_llvm_ir` instead of this field")] + verify_llvm_ir: bool = (false, parse_bool, [TRACKED], + "verify LLVM IR (default: no)"), + virtual_function_elimination: bool = (false, parse_bool, [TRACKED], + "enables dead virtual function elimination optimization. \ + Requires `-Clto[=[fat,yes]]`"), + wasi_exec_model: Option = (None, parse_wasi_exec_model, [TRACKED], + "whether to build a wasi command or reactor"), + // This option only still exists to provide a more gradual transition path for people who need + // the spec-complaint C ABI to be used. + // FIXME remove this after a couple releases + wasm_c_abi: () = ((), parse_wasm_c_abi, [TRACKED], + "use spec-compliant C ABI for `wasm32-unknown-unknown` (deprecated, always enabled)"), + write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED], + "whether long type names should be written to files instead of being printed in errors"), + // tidy-alphabetical-end + + // If you add a new option, please update: + // - compiler/rustc_interface/src/tests.rs + // - src/doc/unstable-book/src/compiler-flags +} diff --git a/src/tools/unstable-book-gen/Cargo.toml b/src/tools/unstable-book-gen/Cargo.toml index 73e5a91bec70a..12dd2eda3d9e2 100644 --- a/src/tools/unstable-book-gen/Cargo.toml +++ b/src/tools/unstable-book-gen/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" [dependencies] tidy = { path = "../tidy" } +proc-macro2 = { version = "1.0", features = ["span-locations"] } +syn = { version = "2.0", features = ["full", "parsing"] } # not actually needed but required for now to unify the feature selection of # `num-traits` between this and `rustbook` diff --git a/src/tools/unstable-book-gen/src/main.rs b/src/tools/unstable-book-gen/src/main.rs index 12b35a11c0d66..8985072cbd27b 100644 --- a/src/tools/unstable-book-gen/src/main.rs +++ b/src/tools/unstable-book-gen/src/main.rs @@ -5,6 +5,9 @@ use std::env; use std::fs::{self, write}; use std::path::Path; +use proc_macro2::{Span, TokenStream, TokenTree}; +use syn::parse::{Parse, ParseStream}; +use syn::{Attribute, Ident, Item, LitStr, Token, parenthesized}; use tidy::diagnostics::RunningCheck; use tidy::features::{ Feature, Features, Status, collect_env_vars, collect_lang_features, collect_lib_features, @@ -116,7 +119,7 @@ fn copy_recursive(from: &Path, to: &Path) { } fn collect_compiler_flags(compiler_path: &Path) -> Features { - let options_path = compiler_path.join("rustc_session/src/options.rs"); + let options_path = compiler_path.join("rustc_session/src/options/unstable.rs"); let options_rs = t!(fs::read_to_string(&options_path), options_path); parse_compiler_flags(&options_rs, &options_path) } @@ -125,50 +128,103 @@ const DESCRIPTION_FIELD: usize = 3; const REQUIRED_FIELDS: usize = 4; const OPTIONAL_FIELDS: usize = 5; -struct SourceBlock<'a> { - content: &'a str, - offset: usize, +struct ParsedOptionEntry { + name: String, + line: usize, + description: String, } -struct ParsedOptionEntry<'a> { - name: &'a str, - name_start: usize, - description: Option, - next_idx: usize, +struct UnstableOptionsInput { + struct_name: Ident, + entries: Vec, } -fn parse_compiler_flags(options_rs: &str, options_path: &Path) -> Features { - let options_block = find_options_block(options_rs, "UnstableOptions"); - let alphabetical_section = find_tidy_alphabetical_section(options_block); - let section_line_offset = line_number(options_rs, alphabetical_section.offset) - 1; - - let mut features = Features::new(); - let mut idx = 0; +impl Parse for ParsedOptionEntry { + fn parse(input: ParseStream<'_>) -> syn::Result { + let _attrs = input.call(Attribute::parse_outer)?; + + let name: Ident = input.parse()?; + let line = name.span().start().line; + input.parse::()?; + let _ty: syn::Type = input.parse()?; + input.parse::()?; + + let tuple_content; + parenthesized!(tuple_content in input); + let tuple_tokens: TokenStream = tuple_content.parse()?; + let tuple_fields = split_tuple_fields(tuple_tokens); + + if !matches!(tuple_fields.len(), REQUIRED_FIELDS | OPTIONAL_FIELDS) { + return Err(syn::Error::new( + name.span(), + format!( + "unexpected field count for option `{name}`: expected 4 or 5, found {}", + tuple_fields.len() + ), + )); + } - while idx < alphabetical_section.content.len() { - skip_ws_comments_and_attrs(alphabetical_section.content, &mut idx); - if idx >= alphabetical_section.content.len() { - break; + if tuple_fields.len() == OPTIONAL_FIELDS + && !is_deprecated_marker_field(&tuple_fields[REQUIRED_FIELDS]) + { + return Err(syn::Error::new( + name.span(), + format!( + "unexpected trailing field in option `{name}`: expected `is_deprecated_and_do_nothing: ...`" + ), + )); } - let entry = parse_one_entry(alphabetical_section.content, idx); - idx = entry.next_idx; + let description = parse_description_field(&tuple_fields[DESCRIPTION_FIELD], &name)?; + Ok(Self { name: name.to_string(), line, description }) + } +} + +impl Parse for UnstableOptionsInput { + fn parse(input: ParseStream<'_>) -> syn::Result { + let struct_name: Ident = input.parse()?; + input.parse::()?; + let _tmod_enum_name: Ident = input.parse()?; + input.parse::()?; + let _stat_name: Ident = input.parse()?; + input.parse::()?; + let _opt_module_name: Ident = input.parse()?; + input.parse::()?; + let _prefix: LitStr = input.parse()?; + input.parse::()?; + let _output_name: LitStr = input.parse()?; + input.parse::()?; + + let entries = + syn::punctuated::Punctuated::::parse_terminated(input)? + .into_iter() + .collect(); + + Ok(Self { struct_name, entries }) + } +} + +fn parse_compiler_flags(options_rs: &str, options_path: &Path) -> Features { + let options_input = parse_unstable_options_macro(options_rs).unwrap_or_else(|error| { + panic!("failed to parse unstable options from `{}`: {error}", options_path.display()) + }); + let mut features = Features::new(); + for entry in options_input.entries { if entry.name == "help" { continue; } features.insert( - entry.name.to_owned(), + entry.name, Feature { level: Status::Unstable, since: None, has_gate_test: false, tracking_issue: None, file: options_path.to_path_buf(), - line: section_line_offset - + line_number(alphabetical_section.content, entry.name_start), - description: entry.description, + line: entry.line, + description: Some(entry.description), }, ); } @@ -176,447 +232,72 @@ fn parse_compiler_flags(options_rs: &str, options_path: &Path) -> Features { features } -fn parse_one_entry(source: &str, start_idx: usize) -> ParsedOptionEntry<'_> { - let name_start = start_idx; - let name_end = - parse_ident_end(source, name_start).expect("expected an option name in UnstableOptions"); - let name = &source[name_start..name_end]; - let mut idx = name_end; - - skip_ws_comments(source, &mut idx); - expect_byte(source, idx, b':', &format!("expected `:` after option name `{name}`")); - idx += 1; - - idx = - find_char_outside_nested(source, idx, b'=').expect("expected `=` in UnstableOptions entry"); - idx += 1; - - skip_ws_comments(source, &mut idx); - expect_byte(source, idx, b'(', &format!("expected tuple payload for option `{name}`")); - - let tuple_start = idx; - let tuple_end = find_matching_delimiter(source, tuple_start, b'(', b')') - .expect("UnstableOptions tuple should be balanced"); - let next_idx = skip_past_entry_delimiter(source, tuple_end + 1, name); - - let description = if name == "help" { - None - } else { - let fields = split_top_level_fields(&source[tuple_start + 1..tuple_end]); - validate_option_fields(&fields, name); - // The `options!` macro layout is `(init, parse, [dep_tracking...], desc, ...)`. - Some(parse_string_literal( - fields.get(DESCRIPTION_FIELD).expect("option description should be present"), - )) - }; - - ParsedOptionEntry { name, name_start, description, next_idx } -} - -fn find_options_block<'a>(source: &'a str, struct_name: &str) -> SourceBlock<'a> { - let mut search_from = 0; - - while let Some(relative_start) = source[search_from..].find("options!") { - let macro_start = search_from + relative_start; - let open_brace = source[macro_start..] - .find('{') - .map(|relative| macro_start + relative) - .expect("options! invocation should contain `{`"); - let close_brace = find_matching_delimiter(source, open_brace, b'{', b'}') - .expect("options! invocation should have a matching `}`"); - let block = &source[open_brace + 1..close_brace]; - - if block.trim_start().starts_with(struct_name) { - return SourceBlock { content: block, offset: open_brace + 1 }; - } - - search_from = close_brace + 1; - } - - panic!("could not find `{struct_name}` options! block"); -} - -fn find_tidy_alphabetical_section(block: SourceBlock<'_>) -> SourceBlock<'_> { - let start_marker = "// tidy-alphabetical-start"; - let end_marker = "// tidy-alphabetical-end"; - - let section_start = block - .content - .find(start_marker) - .map(|start| start + start_marker.len()) - .expect("options! block should contain `// tidy-alphabetical-start`"); - let section_end = block.content[section_start..] - .find(end_marker) - .map(|end| section_start + end) - .expect("options! block should contain `// tidy-alphabetical-end`"); - - SourceBlock { - content: &block.content[section_start..section_end], - offset: block.offset + section_start, - } -} - -fn line_number(source: &str, offset: usize) -> usize { - source[..offset].bytes().filter(|&byte| byte == b'\n').count() + 1 -} - -fn expect_byte(source: &str, idx: usize, expected: u8, context: &str) { - assert_eq!(source.as_bytes().get(idx).copied(), Some(expected), "{context}"); -} - -fn skip_ws_comments_and_attrs(source: &str, idx: &mut usize) { - loop { - skip_ws_comments(source, idx); - - if source[*idx..].starts_with("#[") { - let attr_start = *idx + 1; - let attr_end = find_matching_delimiter(source, attr_start, b'[', b']') - .expect("attribute should have matching `]`"); - *idx = attr_end + 1; - continue; - } - - break; - } -} - -fn skip_ws_comments(source: &str, idx: &mut usize) { - loop { - while let Some(byte) = source.as_bytes().get(*idx) { - if byte.is_ascii_whitespace() { - *idx += 1; - } else { - break; - } - } +fn parse_unstable_options_macro(source: &str) -> syn::Result { + let ast = syn::parse_file(source)?; - if source[*idx..].starts_with("//") { - *idx = source[*idx..].find('\n').map_or(source.len(), |end| *idx + end + 1); + for item in ast.items { + let Item::Macro(item_macro) = item else { continue; - } + }; - if source[*idx..].starts_with("/*") { - *idx = skip_block_comment(source, *idx); + if !item_macro.mac.path.is_ident("options") { continue; } - break; - } -} - -fn skip_block_comment(source: &str, mut idx: usize) -> usize { - let mut depth = 1; - idx += 2; - - while idx < source.len() { - match source.as_bytes().get(idx..idx + 2) { - Some(b"/*") => { - depth += 1; - idx += 2; - } - Some(b"*/") => { - depth -= 1; - idx += 2; - if depth == 0 { - return idx; - } - } - _ => idx += 1, - } - } - - panic!("unterminated block comment"); -} - -fn parse_ident_end(source: &str, start: usize) -> Option { - let bytes = source.as_bytes(); - let first = *bytes.get(start)?; - if !(first == b'_' || first.is_ascii_alphabetic()) { - return None; - } - - let mut idx = start + 1; - while let Some(byte) = bytes.get(idx) { - if *byte == b'_' || byte.is_ascii_alphanumeric() { - idx += 1; - } else { - break; - } - } - - Some(idx) -} - -fn find_char_outside_nested(source: &str, start: usize, needle: u8) -> Option { - let mut idx = start; - let mut paren_depth = 0; - let mut bracket_depth = 0; - let mut brace_depth = 0; - - while idx < source.len() { - match source.as_bytes()[idx] { - b'/' if source[idx..].starts_with("//") => { - idx = source[idx..].find('\n').map_or(source.len(), |end| idx + end + 1); - } - b'/' if source[idx..].starts_with("/*") => idx = skip_block_comment(source, idx), - b'"' => idx = skip_string_literal(source, idx), - b'(' => { - paren_depth += 1; - idx += 1; - } - b')' => { - paren_depth -= 1; - idx += 1; - } - b'[' => { - bracket_depth += 1; - idx += 1; - } - b']' => { - bracket_depth -= 1; - idx += 1; - } - b'{' => { - brace_depth += 1; - idx += 1; - } - b'}' => { - brace_depth -= 1; - idx += 1; - } - byte if byte == needle - && paren_depth == 0 - && bracket_depth == 0 - && brace_depth == 0 => - { - return Some(idx); - } - _ => idx += 1, + let parsed = syn::parse2::(item_macro.mac.tokens)?; + if parsed.struct_name == "UnstableOptions" { + return Ok(parsed); } } - None + Err(syn::Error::new( + Span::call_site(), + "could not find `options!` invocation for `UnstableOptions`", + )) } -fn find_matching_delimiter(source: &str, start: usize, open: u8, close: u8) -> Option { - let mut idx = start; - let mut depth = 0; - - while idx < source.len() { - match source.as_bytes()[idx] { - b'/' if source[idx..].starts_with("//") => { - idx = source[idx..].find('\n').map_or(source.len(), |end| idx + end + 1); - } - b'/' if source[idx..].starts_with("/*") => idx = skip_block_comment(source, idx), - b'"' => idx = skip_string_literal(source, idx), - byte if byte == open => { - depth += 1; - idx += 1; - } - byte if byte == close => { - depth -= 1; - if depth == 0 { - return Some(idx); - } - idx += 1; - } - _ => idx += 1, - } - } - - None +fn parse_description_field(field: &TokenStream, option_name: &Ident) -> syn::Result { + let lit = syn::parse2::(field.clone()).map_err(|_| { + syn::Error::new_spanned( + field.clone(), + format!("expected description string literal in option `{option_name}`"), + ) + })?; + Ok(lit.value()) } -fn split_top_level_fields(source: &str) -> Vec<&str> { +fn split_tuple_fields(tuple_tokens: TokenStream) -> Vec { let mut fields = Vec::new(); - let mut field_start = 0; - let mut idx = 0; - let mut paren_depth = 0; - let mut bracket_depth = 0; - let mut brace_depth = 0; - - while idx < source.len() { - match source.as_bytes()[idx] { - b'/' if source[idx..].starts_with("//") => { - idx = source[idx..].find('\n').map_or(source.len(), |end| idx + end + 1); - } - b'/' if source[idx..].starts_with("/*") => idx = skip_block_comment(source, idx), - b'"' => idx = skip_string_literal(source, idx), - b'(' => { - paren_depth += 1; - idx += 1; - } - b')' => { - paren_depth -= 1; - idx += 1; - } - b'[' => { - bracket_depth += 1; - idx += 1; - } - b']' => { - bracket_depth -= 1; - idx += 1; - } - b'{' => { - brace_depth += 1; - idx += 1; - } - b'}' => { - brace_depth -= 1; - idx += 1; - } - b',' if paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 => { - fields.push(source[field_start..idx].trim()); - idx += 1; - field_start = idx; - } - _ => idx += 1, - } - } - - fields.push(source[field_start..].trim()); - fields -} - -fn validate_option_fields(fields: &[&str], name: &str) { - assert!( - matches!(fields.len(), REQUIRED_FIELDS | OPTIONAL_FIELDS), - "unexpected field count for option `{name}`: expected 4 or 5 fields, found {}", - fields.len() - ); - assert!( - fields[2].starts_with('[') && fields[2].ends_with(']'), - "expected dep-tracking field in option `{name}`, found `{}`", - fields[2] - ); - assert!( - looks_like_string_literal(fields[DESCRIPTION_FIELD]), - "expected description string literal in option `{name}`, found `{}`", - fields[DESCRIPTION_FIELD] - ); - - if let Some(extra_field) = fields.get(REQUIRED_FIELDS) { - assert!( - extra_field.trim_start().starts_with("is_deprecated_and_do_nothing:"), - "unexpected trailing field in option `{name}`: `{extra_field}`", - ); - } -} - -fn looks_like_string_literal(field: &str) -> bool { - let field = field.trim(); - (field.starts_with('"') && field.ends_with('"')) || parse_raw_string_literal(field).is_some() -} - -fn skip_past_entry_delimiter(source: &str, start: usize, name: &str) -> usize { - let mut idx = start; - skip_ws_comments(source, &mut idx); - - match source.as_bytes().get(idx).copied() { - Some(b',') => idx + 1, - None => idx, - Some(byte) => { - panic!("expected `,` after option entry `{name}`, found {:?}", char::from(byte)) - } - } -} - -fn skip_string_literal(source: &str, mut idx: usize) -> usize { - idx += 1; - - while idx < source.len() { - match source.as_bytes()[idx] { - b'\\' => { - idx += 1; - if idx < source.len() { - idx += 1; - } + let mut current = TokenStream::new(); + + for token in tuple_tokens { + if let TokenTree::Punct(punct) = &token { + if punct.as_char() == ',' { + fields.push(current); + current = TokenStream::new(); + continue; } - b'"' => return idx + 1, - _ => idx += 1, } + current.extend([token]); } + fields.push(current); - panic!("unterminated string literal"); -} - -fn parse_string_literal(literal: &str) -> String { - let literal = literal.trim(); - - if let Some(raw_literal) = parse_raw_string_literal(literal) { - return raw_literal; + while matches!(fields.last(), Some(field) if field.is_empty()) { + fields.pop(); } - let inner = literal - .strip_prefix('"') - .and_then(|value| value.strip_suffix('"')) - .expect("expected a string literal"); - let mut output = String::new(); - let mut chars = inner.chars().peekable(); - - while let Some(ch) = chars.next() { - if ch != '\\' { - output.push(ch); - continue; - } - - let escaped = chars.next().expect("unterminated string escape"); - match escaped { - '\n' => while chars.next_if(|ch| ch.is_whitespace()).is_some() {}, - '\r' => { - let _ = chars.next_if_eq(&'\n'); - while chars.next_if(|ch| ch.is_whitespace()).is_some() {} - } - '"' => output.push('"'), - '\'' => output.push('\''), - '\\' => output.push('\\'), - 'n' => output.push('\n'), - 'r' => output.push('\r'), - 't' => output.push('\t'), - '0' => output.push('\0'), - 'x' => { - let hi = chars.next().expect("missing first hex digit in escape"); - let lo = chars.next().expect("missing second hex digit in escape"); - let byte = u8::from_str_radix(&format!("{hi}{lo}"), 16) - .expect("invalid hex escape in string literal"); - output.push(char::from(byte)); - } - 'u' => { - assert_eq!(chars.next(), Some('{'), "expected `{{` after `\\u`"); - let mut digits = String::new(); - for ch in chars.by_ref() { - if ch == '}' { - break; - } - digits.push(ch); - } - let scalar = - u32::from_str_radix(&digits, 16).expect("invalid unicode escape in string"); - output.push(char::from_u32(scalar).expect("unicode escape should be valid")); - } - _ => panic!("unsupported escape in string literal"), - } - } - - output + fields } -fn parse_raw_string_literal(literal: &str) -> Option { - let rest = literal.strip_prefix('r')?; - let hashes = rest.bytes().take_while(|&byte| byte == b'#').count(); - let quote_idx = 1 + hashes; - - if literal.as_bytes().get(quote_idx) != Some(&b'"') { - return None; - } - - let suffix = format!("\"{}", "#".repeat(hashes)); - let content = literal[quote_idx + 1..] - .strip_suffix(&suffix) - .expect("raw string literal should have a matching terminator"); - - Some(content.to_owned()) +fn is_deprecated_marker_field(field: &TokenStream) -> bool { + let mut tokens = field.clone().into_iter(); + let Some(TokenTree::Ident(name)) = tokens.next() else { + return false; + }; + let Some(TokenTree::Punct(colon)) = tokens.next() else { + return false; + }; + name == "is_deprecated_and_do_nothing" && colon.as_char() == ':' } fn main() { diff --git a/src/tools/unstable-book-gen/src/tests.rs b/src/tools/unstable-book-gen/src/tests.rs index 98008a45be629..9e57f032f737a 100644 --- a/src/tools/unstable-book-gen/src/tests.rs +++ b/src/tools/unstable-book-gen/src/tests.rs @@ -1,83 +1,69 @@ use std::path::{Path, PathBuf}; -use super::{parse_compiler_flags, parse_one_entry, skip_ws_comments_and_attrs}; +use super::parse_compiler_flags; #[test] fn parses_unstable_options_entries() { - let tidy_start = "// tidy-alphabetical-start"; - let tidy_end = "// tidy-alphabetical-end"; - let options_rs = format!( - "\n\ - options! {{\n\ - \x20 UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, \"Z\", \"unstable\",\n\ - \n\ - \x20 {tidy_start}\n\ - \x20 #[rustc_lint_opt_deny_field_access(\"test attr\")]\n\ - \x20 allow_features: Option> = (None, parse_opt_comma_list, [TRACKED],\n\ - \x20 \"only allow the listed language features to be enabled in code (comma separated)\"),\n\ - \x20 dump_mir: Option = (None, parse_opt_string, [UNTRACKED],\n\ - \x20 \"dump MIR state to file.\n\ - \x20 `val` is used to select which passes and functions to dump.\"),\n\ - \x20 join_lines: bool = (false, parse_bool, [TRACKED],\n\ - \x20 \"join \\\n\ - \x20 continued lines\"),\n\ - \x20 help: bool = (false, parse_no_value, [UNTRACKED], \"Print unstable compiler options\"),\n\ - \x20 {tidy_end}\n\ - }}\n" - ); + let options_rs = r#"options! { + UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, "Z", "unstable", + + #[rustc_lint_opt_deny_field_access("test attr")] + allow_features: Option> = (None, parse_opt_comma_list, [TRACKED], + "only allow the listed language features to be enabled in code (comma separated)"), + dump_mir: Option = (None, parse_opt_string, [UNTRACKED], + "dump MIR state to file.\n\ + `val` is used to select which passes and functions to dump."), + join_lines: bool = (false, parse_bool, [TRACKED], + "join \ + continued lines"), + help: bool = (false, parse_no_value, [UNTRACKED], "Print unstable compiler options"), +}"#; - let features = parse_compiler_flags(&options_rs, Path::new("options.rs")); + let features = parse_compiler_flags(options_rs, Path::new("options/unstable.rs")); assert!(features.contains_key("allow_features")); assert!(features.contains_key("dump_mir")); assert!(features.contains_key("join_lines")); assert!(!features.contains_key("help")); + assert!( + features["dump_mir"] + .description + .as_deref() + .expect("dump_mir description should exist") + .starts_with("dump MIR state to file.\n"), + ); + assert_eq!(features["join_lines"].description.as_deref(), Some("join continued lines")); assert_eq!( - features["dump_mir"].description.as_deref(), - Some( - "dump MIR state to file.\n `val` is used to select which passes and functions to dump." - ), + features["allow_features"].description.as_deref(), + Some("only allow the listed language features to be enabled in code (comma separated)"), ); - assert_eq!(features["join_lines"].description.as_deref(), Some("join continued lines"),); - assert_eq!(features["allow_features"].file, PathBuf::from("options.rs")); - assert_eq!(features["allow_features"].line, 7); + assert_eq!(features["allow_features"].file, PathBuf::from("options/unstable.rs")); + assert_eq!(features["allow_features"].line, 5); } #[test] -fn parse_one_entry_skips_help_description_and_advances() { - let section = "\ -help: bool = (false, parse_no_value, [UNTRACKED], \"Print unstable compiler options\"),\n\ -join_lines: bool = (false, parse_bool, [TRACKED], \"join \\\n\ - continued lines\"),\n"; - - let help_entry = parse_one_entry(section, 0); - assert_eq!(help_entry.name, "help"); - assert!(help_entry.description.is_none()); - - let mut next_idx = help_entry.next_idx; - skip_ws_comments_and_attrs(section, &mut next_idx); - let next_entry = parse_one_entry(section, next_idx); +fn parser_accepts_optional_trailing_metadata() { + let options_rs = r##"options! { + UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, "Z", "unstable", - assert_eq!(next_entry.name, "join_lines"); - assert_eq!(next_entry.description.as_deref(), Some("join continued lines"),); -} - -#[test] -fn parse_one_entry_accepts_optional_trailing_metadata() { - let entry = "\ -deprecated_flag: bool = (false, parse_no_value, [UNTRACKED], \"deprecated flag\",\n\ - is_deprecated_and_do_nothing: true),\n"; + deprecated_flag: bool = (false, parse_no_value, [UNTRACKED], "deprecated flag", + is_deprecated_and_do_nothing: true), + raw_description: bool = (false, parse_no_value, [UNTRACKED], r#"raw "quoted" text"#), +}"##; - let parsed = parse_one_entry(entry, 0); - assert_eq!(parsed.name, "deprecated_flag"); - assert_eq!(parsed.description.as_deref(), Some("deprecated flag")); + let features = parse_compiler_flags(options_rs, Path::new("options/unstable.rs")); + assert_eq!(features["deprecated_flag"].description.as_deref(), Some("deprecated flag")); + assert_eq!( + features["raw_description"].description.as_deref(), + Some("raw \"quoted\" text"), + ); } #[test] fn parses_real_unstable_options_file() { let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); - let options_path = manifest_dir.join("../../../compiler/rustc_session/src/options.rs"); + let options_path = manifest_dir.join("../../../compiler/rustc_session/src/options/unstable.rs"); let options_rs = std::fs::read_to_string(&options_path).unwrap(); let features = parse_compiler_flags(&options_rs, &options_path); From deeb45a5debda9f607753d444bb8e3cd4c6b799e Mon Sep 17 00:00:00 2001 From: randomicon00 <20146907+randomicon00@users.noreply.github.com> Date: Sat, 21 Mar 2026 11:57:31 -0400 Subject: [PATCH 05/17] style: fix rustfmt --- src/tools/unstable-book-gen/src/tests.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/tools/unstable-book-gen/src/tests.rs b/src/tools/unstable-book-gen/src/tests.rs index 9e57f032f737a..000cfaebdac30 100644 --- a/src/tools/unstable-book-gen/src/tests.rs +++ b/src/tools/unstable-book-gen/src/tests.rs @@ -54,10 +54,7 @@ fn parser_accepts_optional_trailing_metadata() { let features = parse_compiler_flags(options_rs, Path::new("options/unstable.rs")); assert_eq!(features["deprecated_flag"].description.as_deref(), Some("deprecated flag")); - assert_eq!( - features["raw_description"].description.as_deref(), - Some("raw \"quoted\" text"), - ); + assert_eq!(features["raw_description"].description.as_deref(), Some("raw \"quoted\" text"),); } #[test] From 36c22f7cf7a7070c9c6b478406f9b2ba831560c6 Mon Sep 17 00:00:00 2001 From: jumpiix Date: Mon, 23 Mar 2026 13:08:12 +0100 Subject: [PATCH 06/17] Add test for dead code warnings in const _ Tests that dead code warnings work correctly inside const _ blocks. Issue was already fixed, adding regression test as requested. --- .../const-underscore-issue-101532.rs | 14 ++++++++ .../const-underscore-issue-101532.stderr | 32 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/ui/lint/dead-code/const-underscore-issue-101532.rs create mode 100644 tests/ui/lint/dead-code/const-underscore-issue-101532.stderr diff --git a/tests/ui/lint/dead-code/const-underscore-issue-101532.rs b/tests/ui/lint/dead-code/const-underscore-issue-101532.rs new file mode 100644 index 0000000000000..4dc25fbbecea9 --- /dev/null +++ b/tests/ui/lint/dead-code/const-underscore-issue-101532.rs @@ -0,0 +1,14 @@ +//@ check-pass +// Test for issue #101532 - dead code warnings should work inside const _ + +#![warn(dead_code)] + +const _: () = { + let a: (); + struct B {} //~ WARN struct `B` is never constructed + enum C {} //~ WARN enum `C` is never used + fn d() {} //~ WARN function `d` is never used + const E: () = {}; //~ WARN constant `E` is never used +}; + +fn main() {} diff --git a/tests/ui/lint/dead-code/const-underscore-issue-101532.stderr b/tests/ui/lint/dead-code/const-underscore-issue-101532.stderr new file mode 100644 index 0000000000000..d330b9e9f40a4 --- /dev/null +++ b/tests/ui/lint/dead-code/const-underscore-issue-101532.stderr @@ -0,0 +1,32 @@ +warning: struct `B` is never constructed + --> $DIR/const-underscore-issue-101532.rs:8:12 + | +LL | struct B {} + | ^ + | +note: the lint level is defined here + --> $DIR/const-underscore-issue-101532.rs:4:9 + | +LL | #![warn(dead_code)] + | ^^^^^^^^^ + +warning: enum `C` is never used + --> $DIR/const-underscore-issue-101532.rs:9:10 + | +LL | enum C {} + | ^ + +warning: function `d` is never used + --> $DIR/const-underscore-issue-101532.rs:10:8 + | +LL | fn d() {} + | ^ + +warning: constant `E` is never used + --> $DIR/const-underscore-issue-101532.rs:11:11 + | +LL | const E: () = {}; + | ^ + +warning: 4 warnings emitted + From 75bd926afd3a6dc272f4c6c9835fcaff807cc870 Mon Sep 17 00:00:00 2001 From: randomicon00 <20146907+randomicon00@users.noreply.github.com> Date: Tue, 24 Mar 2026 21:46:19 -0400 Subject: [PATCH 07/17] address PR review feedback for unstable-book-gen --- compiler/rustc_session/src/options.rs | 4 ++++ src/tools/unstable-book-gen/src/main.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 836f7d1cb456f..e8681237a03d8 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -604,6 +604,10 @@ macro_rules! tmod_enum { /// necessary code. The main gotcha of this macro is the `cgsetters` module which is a bunch of /// generated code to parse an option into its respective field in the struct. There are a few /// hand-written parsers for parsing specific types of values in this module. +/// +/// Note: this macro's invocation is also parsed by a `syn`-based parser in +/// `src/tools/unstable-book-gen/src/main.rs` to extract unstable option names and descriptions. +/// If the format of this macro changes, that parser may need to be updated as well. macro_rules! options { ($struct_name:ident, $tmod_enum_name:ident, $stat:ident, $optmod:ident, $prefix:expr, $outputname:expr, $($( #[$attr:meta] )* $opt:ident : $t:ty = ( diff --git a/src/tools/unstable-book-gen/src/main.rs b/src/tools/unstable-book-gen/src/main.rs index 8985072cbd27b..21d4da46bef54 100644 --- a/src/tools/unstable-book-gen/src/main.rs +++ b/src/tools/unstable-book-gen/src/main.rs @@ -158,7 +158,7 @@ impl Parse for ParsedOptionEntry { return Err(syn::Error::new( name.span(), format!( - "unexpected field count for option `{name}`: expected 4 or 5, found {}", + "unexpected field count for option `{name}`: expected {REQUIRED_FIELDS} or {OPTIONAL_FIELDS}, found {}", tuple_fields.len() ), )); From 16b9fddf001a7f024f300e2bffa43203ff271318 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Mar 2026 11:42:38 +0800 Subject: [PATCH 08/17] Improve doc comment unicode guidance --- compiler/rustc_parse/src/errors.rs | 10 +++++++--- compiler/rustc_parse/src/lexer/mod.rs | 5 ++++- .../macro/unicode-control-codepoints-macros.stderr | 10 +++++----- tests/ui/parser/unicode-control-codepoints.stderr | 4 ++-- .../unicode-control-doc-comment-issue-153096.rs | 8 ++++++++ .../unicode-control-doc-comment-issue-153096.stderr | 13 +++++++++++++ 6 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 tests/ui/parser/unicode-control-doc-comment-issue-153096.rs create mode 100644 tests/ui/parser/unicode-control-doc-comment-issue-153096.stderr diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 0e852d2abda04..6faaafcc01005 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -4344,7 +4344,7 @@ impl Subdiagnostic for HiddenUnicodeCodepointsDiagLabels { pub(crate) enum HiddenUnicodeCodepointsDiagSub { Escape { spans: Vec<(char, Span)> }, - NoEscape { spans: Vec<(char, Span)> }, + NoEscape { spans: Vec<(char, Span)>, is_doc_comment: bool }, } // Used because of multiple multipart_suggestion and note @@ -4370,7 +4370,7 @@ impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub { Applicability::MachineApplicable, ); } - HiddenUnicodeCodepointsDiagSub::NoEscape { spans } => { + HiddenUnicodeCodepointsDiagSub::NoEscape { spans, is_doc_comment } => { // FIXME: in other suggestions we've reversed the inner spans of doc comments. We // should do the same here to provide the same good suggestions as we do for // literals above. @@ -4383,7 +4383,11 @@ impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub { .join(", "), ); diag.note(msg!("if their presence wasn't intentional, you can remove them")); - diag.note(msg!("if you want to keep them but make them visible in your source code, you can escape them: {$escaped}")); + if is_doc_comment { + diag.note(msg!(r#"if you need to keep them and make them explicit in source, rewrite this doc comment as a `#[doc = "..."]` attribute and use Unicode escapes such as {$escaped}"#)); + } else { + diag.note(msg!("if you want to keep them but make them visible in your source code, you can escape them: {$escaped}")); + } } } } diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 2223e6a265759..5766d25bc86ce 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -546,6 +546,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { self.mk_sp(start, self.pos), 0, false, + true, "doc comment", ); } @@ -580,6 +581,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { span, padding, point_at_inner_spans, + false, label, ); } @@ -590,6 +592,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { span: Span, padding: u32, point_at_inner_spans: bool, + is_doc_comment: bool, label: &str, ) { // Obtain the `Span`s for each of the forbidden chars. @@ -610,7 +613,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let sub = if point_at_inner_spans && !spans.is_empty() { errors::HiddenUnicodeCodepointsDiagSub::Escape { spans } } else { - errors::HiddenUnicodeCodepointsDiagSub::NoEscape { spans } + errors::HiddenUnicodeCodepointsDiagSub::NoEscape { spans, is_doc_comment } }; self.psess.buffer_lint( diff --git a/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr b/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr index 22fb1b945c654..b1d9c9d018744 100644 --- a/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr +++ b/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr @@ -6,7 +6,7 @@ LL | /// �test� RTL in doc in vec | = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen = note: if their presence wasn't intentional, you can remove them - = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + = note: if you need to keep them and make them explicit in source, rewrite this doc comment as a `#[doc = "..."]` attribute and use Unicode escapes such as '\u{202e}', '\u{2066}' = note: `#[deny(text_direction_codepoint_in_literal)]` on by default error: unicode codepoint changing visible direction of text present in doc comment @@ -19,7 +19,7 @@ LL | | */ | = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen = note: if their presence wasn't intentional, you can remove them - = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + = note: if you need to keep them and make them explicit in source, rewrite this doc comment as a `#[doc = "..."]` attribute and use Unicode escapes such as '\u{202e}', '\u{2066}' error: unicode codepoint changing visible direction of text present in doc comment --> $DIR/unicode-control-codepoints-macros.rs:33:9 @@ -31,7 +31,7 @@ LL | | */ | = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen = note: if their presence wasn't intentional, you can remove them - = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + = note: if you need to keep them and make them explicit in source, rewrite this doc comment as a `#[doc = "..."]` attribute and use Unicode escapes such as '\u{202e}', '\u{2066}' error: unicode codepoint changing visible direction of text present in doc comment --> $DIR/unicode-control-codepoints-macros.rs:41:9 @@ -41,7 +41,7 @@ LL | /// �test� RTL in doc in proc macro | = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen = note: if their presence wasn't intentional, you can remove them - = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + = note: if you need to keep them and make them explicit in source, rewrite this doc comment as a `#[doc = "..."]` attribute and use Unicode escapes such as '\u{202e}', '\u{2066}' error: unicode codepoint changing visible direction of text present in doc comment --> $DIR/unicode-control-codepoints-macros.rs:46:9 @@ -51,7 +51,7 @@ LL | /// �test� RTL in doc in proc macro | = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen = note: if their presence wasn't intentional, you can remove them - = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + = note: if you need to keep them and make them explicit in source, rewrite this doc comment as a `#[doc = "..."]` attribute and use Unicode escapes such as '\u{202e}', '\u{2066}' error: aborting due to 5 previous errors diff --git a/tests/ui/parser/unicode-control-codepoints.stderr b/tests/ui/parser/unicode-control-codepoints.stderr index 7978c1435f609..3b273f8212faa 100644 --- a/tests/ui/parser/unicode-control-codepoints.stderr +++ b/tests/ui/parser/unicode-control-codepoints.stderr @@ -232,7 +232,7 @@ LL | /** '�'); */fn foo() {} | = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen = note: if their presence wasn't intentional, you can remove them - = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}' + = note: if you need to keep them and make them explicit in source, rewrite this doc comment as a `#[doc = "..."]` attribute and use Unicode escapes such as '\u{202e}' error: unicode codepoint changing visible direction of text present in doc comment --> $DIR/unicode-control-codepoints.rs:46:1 @@ -244,7 +244,7 @@ LL | | * '�'); */fn bar() {} | = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen = note: if their presence wasn't intentional, you can remove them - = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}' + = note: if you need to keep them and make them explicit in source, rewrite this doc comment as a `#[doc = "..."]` attribute and use Unicode escapes such as '\u{202e}' error: aborting due to 20 previous errors diff --git a/tests/ui/parser/unicode-control-doc-comment-issue-153096.rs b/tests/ui/parser/unicode-control-doc-comment-issue-153096.rs new file mode 100644 index 0000000000000..a7998daa72fdc --- /dev/null +++ b/tests/ui/parser/unicode-control-doc-comment-issue-153096.rs @@ -0,0 +1,8 @@ +//@ edition: 2024 + +#[allow(unused)] +/// ⁨א⁩, ⁨ב⁩, ⁨ג⁩, ⁨ד⁩, ⁨ה⁩ +//~^ ERROR unicode codepoint changing visible direction of text present in doc comment +fn foo() {} + +fn main() {} diff --git a/tests/ui/parser/unicode-control-doc-comment-issue-153096.stderr b/tests/ui/parser/unicode-control-doc-comment-issue-153096.stderr new file mode 100644 index 0000000000000..668a7844da635 --- /dev/null +++ b/tests/ui/parser/unicode-control-doc-comment-issue-153096.stderr @@ -0,0 +1,13 @@ +error: unicode codepoint changing visible direction of text present in doc comment + --> $DIR/unicode-control-doc-comment-issue-153096.rs:4:1 + | +LL | /// �א�, �ב�, �ג�, �ד�, �ה� + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ this doc comment contains invisible unicode text flow control codepoints + | + = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + = note: if their presence wasn't intentional, you can remove them + = note: if you need to keep them and make them explicit in source, rewrite this doc comment as a `#[doc = "..."]` attribute and use Unicode escapes such as '\u{2068}', '\u{2069}', '\u{2068}', '\u{2069}', '\u{2068}', '\u{2069}', '\u{2068}', '\u{2069}', '\u{2068}', '\u{2069}' + = note: `#[deny(text_direction_codepoint_in_literal)]` on by default + +error: aborting due to 1 previous error + From 8fc058109bacdecdb6e2723d4371ab85670d43d2 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 26 Mar 2026 14:59:13 +0000 Subject: [PATCH 09/17] bootstrap: `-Zjson-target-spec` for synthetic targets --- src/bootstrap/src/core/builder/cargo.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 16065cbdae1fa..b4ef00bd4ea0d 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -122,6 +122,9 @@ impl Cargo { cmd_kind: Kind, ) -> Cargo { let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd_kind); + if target.synthetic { + cargo.arg("-Zjson-target-spec"); + } match cmd_kind { // No need to configure the target linker for these command types. @@ -165,7 +168,11 @@ impl Cargo { target: TargetSelection, cmd_kind: Kind, ) -> Cargo { - builder.cargo(compiler, mode, source_type, target, cmd_kind) + let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd_kind); + if target.synthetic { + cargo.arg("-Zjson-target-spec"); + } + cargo } pub fn rustdocflag(&mut self, arg: &str) -> &mut Cargo { From 741a3a7fc466df22e50f2b8d7dc697d22c8bdd41 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 27 Mar 2026 01:26:51 +0900 Subject: [PATCH 10/17] avoid ICE in explicit reference cast suggestion for unrelated leaf predicates --- .../traits/fulfillment_errors.rs | 12 +++-- .../explicit-reference-cast-unrelated-leaf.rs | 21 +++++++++ ...licit-reference-cast-unrelated-leaf.stderr | 47 +++++++++++++++++++ 3 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 tests/ui/traits/explicit-reference-cast-unrelated-leaf.rs create mode 100644 tests/ui/traits/explicit-reference-cast-unrelated-leaf.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index eeccf1eb0ecfa..d6cfc993c8b83 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -285,14 +285,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut err = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg); let trait_def_id = main_trait_predicate.def_id(); - if self.tcx.is_diagnostic_item(sym::From, trait_def_id) - || self.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id) + let leaf_trait_def_id = leaf_trait_predicate.def_id(); + if (self.tcx.is_diagnostic_item(sym::From, trait_def_id) + || self.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id)) + && (self.tcx.is_diagnostic_item(sym::From, leaf_trait_def_id) + || self.tcx.is_diagnostic_item(sym::TryFrom, leaf_trait_def_id)) { let trait_ref = leaf_trait_predicate.skip_binder().trait_ref; - // Defensive: next-solver may produce fewer args than expected. - if trait_ref.args.len() > 1 { - let found_ty = trait_ref.args.type_at(1); + if let Some(found_ty) = trait_ref.args.get(1).and_then(|arg| arg.as_type()) + { let ty = main_trait_predicate.skip_binder().self_ty(); if let Some(cast_ty) = self.find_explicit_cast_type( diff --git a/tests/ui/traits/explicit-reference-cast-unrelated-leaf.rs b/tests/ui/traits/explicit-reference-cast-unrelated-leaf.rs new file mode 100644 index 0000000000000..83687f37f054b --- /dev/null +++ b/tests/ui/traits/explicit-reference-cast-unrelated-leaf.rs @@ -0,0 +1,21 @@ +trait Output<'a> { + type Type; +} + +struct Wrapper; + +impl Wrapper { + fn do_something_wrapper(self, _: F) + where + for<'a> F: Output<'a>, + for<'a> O: From<>::Type>, + { + } +} + +fn main() { + let wrapper = Wrapper; + wrapper.do_something_wrapper(|value| ()); + //~^ ERROR the trait bound `for<'a> {closure@ + //~| ERROR the trait bound `for<'a> _: From<<{closure@ +} diff --git a/tests/ui/traits/explicit-reference-cast-unrelated-leaf.stderr b/tests/ui/traits/explicit-reference-cast-unrelated-leaf.stderr new file mode 100644 index 0000000000000..1e4cb7f1cd18b --- /dev/null +++ b/tests/ui/traits/explicit-reference-cast-unrelated-leaf.stderr @@ -0,0 +1,47 @@ +error[E0277]: the trait bound `for<'a> {closure@$DIR/explicit-reference-cast-unrelated-leaf.rs:18:34: 18:41}: Output<'a>` is not satisfied + --> $DIR/explicit-reference-cast-unrelated-leaf.rs:18:34 + | +LL | wrapper.do_something_wrapper(|value| ()); + | -------------------- ^^^^^^^^^^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | + = help: the trait `for<'a> Output<'a>` is not implemented for closure `{closure@$DIR/explicit-reference-cast-unrelated-leaf.rs:18:34: 18:41}` +help: this trait has no implementations, consider adding one + --> $DIR/explicit-reference-cast-unrelated-leaf.rs:1:1 + | +LL | trait Output<'a> { + | ^^^^^^^^^^^^^^^^ +note: required by a bound in `Wrapper::do_something_wrapper` + --> $DIR/explicit-reference-cast-unrelated-leaf.rs:10:20 + | +LL | fn do_something_wrapper(self, _: F) + | -------------------- required by a bound in this associated function +LL | where +LL | for<'a> F: Output<'a>, + | ^^^^^^^^^^ required by this bound in `Wrapper::do_something_wrapper` + +error[E0277]: the trait bound `for<'a> _: From<<{closure@$DIR/explicit-reference-cast-unrelated-leaf.rs:18:34: 18:41} as Output<'a>>::Type>` is not satisfied + --> $DIR/explicit-reference-cast-unrelated-leaf.rs:18:13 + | +LL | wrapper.do_something_wrapper(|value| ()); + | ^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | + = help: the trait `for<'a> Output<'a>` is not implemented for closure `{closure@$DIR/explicit-reference-cast-unrelated-leaf.rs:18:34: 18:41}` +help: this trait has no implementations, consider adding one + --> $DIR/explicit-reference-cast-unrelated-leaf.rs:1:1 + | +LL | trait Output<'a> { + | ^^^^^^^^^^^^^^^^ +note: required by a bound in `Wrapper::do_something_wrapper` + --> $DIR/explicit-reference-cast-unrelated-leaf.rs:11:20 + | +LL | fn do_something_wrapper(self, _: F) + | -------------------- required by a bound in this associated function +... +LL | for<'a> O: From<>::Type>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Wrapper::do_something_wrapper` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From 5e4e8da4851b92c18f31df0c63f075219ad4e5ff Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Thu, 26 Mar 2026 19:39:05 +0100 Subject: [PATCH 11/17] bootstrap.example.toml: Hint how to allow `build.warnings` This commit turns "Actual" into "Expected" for the following problem: Steps: 1. Do some quick and experimental local code changes in the compiler code. 2. Run `./x build`. 3. Observe `error: warnings are denied by `build.warnings` configuration`. 4. Decide to not care and want to allow warnings. 5. Search for `build.warnings` in **bootstrap.example.toml** Actual: No hits. You get frustrated because didn't learn how to allow warnings. Expected: You get a hit and can easily see how to allow warnings. --- bootstrap.example.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index f3b2a64df853b..25e2ca5948bfb 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -826,7 +826,8 @@ # in the sysroot. It is required for running nvptx tests. #rust.llvm-bitcode-linker = false -# Whether to deny warnings in crates +# Whether to deny warnings in crates. Set to `false` to avoid +# error: warnings are denied by `build.warnings` configuration #rust.deny-warnings = true # Print backtrace on internal compiler errors during bootstrap From 7e95a22e1f6c3aae57da66c38c0b1bc0f1207893 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Mar 2026 22:02:11 +0800 Subject: [PATCH 12/17] rustdoc rejects html emits with json output --- src/librustdoc/config.rs | 12 ++++++++++++ ...tput-format-json-emit-html.html_non_static.stderr | 2 ++ .../output-format-json-emit-html.html_static.stderr | 2 ++ tests/rustdoc-ui/output-format-json-emit-html.rs | 8 ++++++++ .../show-coverage-json-emit-html-non-static.rs | 5 +++++ .../show-coverage-json-emit-html-non-static.stderr | 2 ++ 6 files changed, 31 insertions(+) create mode 100644 tests/rustdoc-ui/output-format-json-emit-html.html_non_static.stderr create mode 100644 tests/rustdoc-ui/output-format-json-emit-html.html_static.stderr create mode 100644 tests/rustdoc-ui/output-format-json-emit-html.rs create mode 100644 tests/rustdoc-ui/show-coverage-json-emit-html-non-static.rs create mode 100644 tests/rustdoc-ui/show-coverage-json-emit-html-non-static.stderr diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index d721034c2d71d..3caff6edd504a 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -522,6 +522,18 @@ impl Options { } } + if output_format == OutputFormat::Json { + if let Some(emit_flag) = emit.iter().find_map(|emit| match emit { + EmitType::HtmlStaticFiles => Some("html-static-files"), + EmitType::HtmlNonStaticFiles => Some("html-non-static-files"), + EmitType::DepInfo(_) => None, + }) { + dcx.fatal(format!( + "the `--emit={emit_flag}` flag is not supported with `--output-format=json`", + )); + } + } + let to_check = matches.opt_strs("check-theme"); if !to_check.is_empty() { let mut content = diff --git a/tests/rustdoc-ui/output-format-json-emit-html.html_non_static.stderr b/tests/rustdoc-ui/output-format-json-emit-html.html_non_static.stderr new file mode 100644 index 0000000000000..8d8e8c6d12281 --- /dev/null +++ b/tests/rustdoc-ui/output-format-json-emit-html.html_non_static.stderr @@ -0,0 +1,2 @@ +error: the `--emit=html-non-static-files` flag is not supported with `--output-format=json` + diff --git a/tests/rustdoc-ui/output-format-json-emit-html.html_static.stderr b/tests/rustdoc-ui/output-format-json-emit-html.html_static.stderr new file mode 100644 index 0000000000000..bc1ddc1b155d1 --- /dev/null +++ b/tests/rustdoc-ui/output-format-json-emit-html.html_static.stderr @@ -0,0 +1,2 @@ +error: the `--emit=html-static-files` flag is not supported with `--output-format=json` + diff --git a/tests/rustdoc-ui/output-format-json-emit-html.rs b/tests/rustdoc-ui/output-format-json-emit-html.rs new file mode 100644 index 0000000000000..7a99cbd91ba6e --- /dev/null +++ b/tests/rustdoc-ui/output-format-json-emit-html.rs @@ -0,0 +1,8 @@ +//@ revisions: html_static html_non_static +//@ check-fail +//@[html_static] compile-flags: -Z unstable-options --output-format=json --emit=html-static-files +//@[html_non_static] compile-flags: -Z unstable-options --output-format=json --emit=html-non-static-files +//[html_static]~? ERROR the `--emit=html-static-files` flag is not supported with `--output-format=json` +//[html_non_static]~? ERROR the `--emit=html-non-static-files` flag is not supported with `--output-format=json` + +pub struct Foo; diff --git a/tests/rustdoc-ui/show-coverage-json-emit-html-non-static.rs b/tests/rustdoc-ui/show-coverage-json-emit-html-non-static.rs new file mode 100644 index 0000000000000..b0f67a3b4195e --- /dev/null +++ b/tests/rustdoc-ui/show-coverage-json-emit-html-non-static.rs @@ -0,0 +1,5 @@ +//@ compile-flags: -Z unstable-options --show-coverage --output-format=json --emit=html-non-static-files +//@ check-fail +//~? ERROR the `--emit=html-non-static-files` flag is not supported with `--output-format=json` + +pub struct Foo; diff --git a/tests/rustdoc-ui/show-coverage-json-emit-html-non-static.stderr b/tests/rustdoc-ui/show-coverage-json-emit-html-non-static.stderr new file mode 100644 index 0000000000000..8d8e8c6d12281 --- /dev/null +++ b/tests/rustdoc-ui/show-coverage-json-emit-html-non-static.stderr @@ -0,0 +1,2 @@ +error: the `--emit=html-non-static-files` flag is not supported with `--output-format=json` + From a53356ec89a20217e0968f19693e50e25beda128 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Fri, 27 Mar 2026 01:11:27 -0400 Subject: [PATCH 13/17] fix: [rustfmt] prevent panic when rewritng associated item delegations Fixes a rustfmt bug for `#![feature(fn_delegation)]` --- src/tools/rustfmt/src/visitor.rs | 12 ++++++++---- src/tools/rustfmt/tests/target/issue_6513.rs | 10 ++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 src/tools/rustfmt/tests/target/issue_6513.rs diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs index 6048084c8da68..560d62e9d57d7 100644 --- a/src/tools/rustfmt/src/visitor.rs +++ b/src/tools/rustfmt/src/visitor.rs @@ -665,11 +665,11 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } // TODO(calebcartwright): consider enabling box_patterns feature gate - match (&ai.kind, visitor_kind) { - (ast::AssocItemKind::Const(c), AssocTraitItem) => { + match (&ai.kind, assoc_ctxt) { + (ast::AssocItemKind::Const(c), visit::AssocCtxt::Trait) => { self.visit_static(&StaticParts::from_trait_item(ai, c.ident)) } - (ast::AssocItemKind::Const(c), AssocImplItem) => { + (ast::AssocItemKind::Const(c), visit::AssocCtxt::Impl { .. }) => { self.visit_static(&StaticParts::from_impl_item(ai, c.ident)) } (ast::AssocItemKind::Fn(ref fn_kind), _) => { @@ -714,7 +714,11 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { (ast::AssocItemKind::MacCall(ref mac), _) => { self.visit_mac(mac, MacroPosition::Item); } - _ => unreachable!(), + (ast::AssocItemKind::Delegation(_) | ast::AssocItemKind::DelegationMac(_), _) => { + // TODO(ytmimi) #![feature(fn_delegation)] + // add formatting for `AssocItemKind::Delegation` and `AssocItemKind::DelegationMac` + self.push_rewrite(ai.span, None); + } } } diff --git a/src/tools/rustfmt/tests/target/issue_6513.rs b/src/tools/rustfmt/tests/target/issue_6513.rs new file mode 100644 index 0000000000000..827d4ccdf27fd --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue_6513.rs @@ -0,0 +1,10 @@ +#![feature(fn_delegation)] + +struct Ty; +impl Ty { + reuse std::convert::identity; +} + +trait Trait { + reuse std::convert::identity; +} From 08bb61bb88026e10468af994dc3a35270dcc072b Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Fri, 27 Mar 2026 15:17:00 +0300 Subject: [PATCH 14/17] Use `tcx.is_typeck_child()` more often --- compiler/rustc_borrowck/src/universal_regions.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index aeba5ee70cf17..16f00e1442ac0 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -476,12 +476,10 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let mut indices = self.compute_indices(fr_static, defining_ty); debug!("build: indices={:?}", indices); - let typeck_root_def_id = self.infcx.tcx.typeck_root_def_id(self.mir_def.to_def_id()); - // If this is a 'root' body (not a closure/coroutine/inline const), then // there are no extern regions, so the local regions start at the same // position as the (empty) sub-list of extern regions - let first_local_index = if self.mir_def.to_def_id() == typeck_root_def_id { + let first_local_index = if !self.infcx.tcx.is_typeck_child(self.mir_def.to_def_id()) { first_extern_index } else { // If this is a closure, coroutine, or inline-const, then the late-bound regions from the enclosing @@ -948,16 +946,14 @@ fn for_each_late_bound_region_in_recursive_scope<'tcx>( mut mir_def_id: LocalDefId, mut f: impl FnMut(ty::Region<'tcx>), ) { - let typeck_root_def_id = tcx.typeck_root_def_id(mir_def_id.to_def_id()); - // Walk up the tree, collecting late-bound regions until we hit the typeck root loop { for_each_late_bound_region_in_item(tcx, mir_def_id, &mut f); - if mir_def_id.to_def_id() == typeck_root_def_id { - break; - } else { + if tcx.is_typeck_child(mir_def_id.to_def_id()) { mir_def_id = tcx.local_parent(mir_def_id); + } else { + break; } } } From 1b68d921a7a5a3e99a8035fec33a53789e40b8aa Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Fri, 27 Mar 2026 15:32:48 +0300 Subject: [PATCH 15/17] Add typeck_root_def_id_local --- .../src/diagnostics/conflict_errors.rs | 6 ++---- compiler/rustc_borrowck/src/diagnostics/mod.rs | 9 +++------ compiler/rustc_borrowck/src/universal_regions.rs | 6 +++--- compiler/rustc_hir_analysis/src/check/region.rs | 8 ++++---- .../rustc_hir_analysis/src/collect/generics_of.rs | 12 ++++++------ compiler/rustc_hir_typeck/src/closure.rs | 2 +- compiler/rustc_hir_typeck/src/lib.rs | 2 +- compiler/rustc_middle/src/queries.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 8 ++------ compiler/rustc_middle/src/ty/util.rs | 14 ++++++++++++++ compiler/rustc_mir_build/src/thir/cx/expr.rs | 12 ++++++------ compiler/rustc_mir_transform/src/lib.rs | 8 ++++---- compiler/rustc_mir_transform/src/liveness.rs | 2 +- compiler/rustc_monomorphize/src/collector.rs | 2 +- .../src/error_reporting/infer/need_type_info.rs | 6 +++--- compiler/rustc_ty_utils/src/nested_bodies.rs | 9 ++++----- src/tools/clippy/clippy_lints/src/shadow.rs | 2 +- 17 files changed, 57 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7bbce730c1658..a63c54a2618b7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1569,10 +1569,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let tcx = self.infcx.tcx; let generics = tcx.generics_of(self.mir_def_id()); - let Some(hir_generics) = tcx - .typeck_root_def_id(self.mir_def_id().to_def_id()) - .as_local() - .and_then(|def_id| tcx.hir_get_generics(def_id)) + let Some(hir_generics) = + tcx.hir_get_generics(tcx.typeck_root_def_id_local(self.mir_def_id())) else { return; }; diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 8b4bda8f7fefc..86d7119639a3f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1274,12 +1274,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let ty::Param(param_ty) = *self_ty.kind() && let generics = self.infcx.tcx.generics_of(self.mir_def_id()) && let param = generics.type_param(param_ty, self.infcx.tcx) - && let Some(hir_generics) = self - .infcx - .tcx - .typeck_root_def_id(self.mir_def_id().to_def_id()) - .as_local() - .and_then(|def_id| self.infcx.tcx.hir_get_generics(def_id)) + && let Some(hir_generics) = self.infcx.tcx.hir_get_generics( + self.infcx.tcx.typeck_root_def_id_local(self.mir_def_id()), + ) && let spans = hir_generics .predicates .iter() diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 16f00e1442ac0..e96c6c7c56f9b 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -581,7 +581,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { /// see `DefiningTy` for details. fn defining_ty(&self) -> DefiningTy<'tcx> { let tcx = self.infcx.tcx; - let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.to_def_id()); + let typeck_root_def_id = tcx.typeck_root_def_id_local(self.mir_def); match tcx.hir_body_owner_kind(self.mir_def) { BodyOwnerKind::Closure | BodyOwnerKind::Fn => { @@ -612,7 +612,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { BodyOwnerKind::Const { .. } | BodyOwnerKind::Static(..) => { let identity_args = GenericArgs::identity_for_item(tcx, typeck_root_def_id); - if self.mir_def.to_def_id() == typeck_root_def_id { + if self.mir_def == typeck_root_def_id { let args = self.infcx.replace_free_regions_with_nll_infer_vars( NllRegionVariableOrigin::FreeRegion, identity_args, @@ -658,7 +658,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { defining_ty: DefiningTy<'tcx>, ) -> UniversalRegionIndices<'tcx> { let tcx = self.infcx.tcx; - let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.to_def_id()); + let typeck_root_def_id = tcx.typeck_root_def_id_local(self.mir_def); let identity_args = GenericArgs::identity_for_item(tcx, typeck_root_def_id); let renumbered_args = defining_ty.args(); diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 8865638e3af2f..e31beca390837 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -11,7 +11,7 @@ use std::mem; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Arm, Block, Expr, LetStmt, Pat, PatKind, Stmt}; use rustc_index::Idx; @@ -849,13 +849,13 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> { /// re-use in incremental scenarios. We may sometimes need to rerun the /// type checker even when the HIR hasn't changed, and in those cases /// we can avoid reconstructing the region scope tree. -pub(crate) fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { - let typeck_root_def_id = tcx.typeck_root_def_id(def_id); +pub(crate) fn region_scope_tree(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &ScopeTree { + let typeck_root_def_id = tcx.typeck_root_def_id_local(def_id); if typeck_root_def_id != def_id { return tcx.region_scope_tree(typeck_root_def_id); } - let scope_tree = if let Some(body) = tcx.hir_maybe_body_owned_by(def_id.expect_local()) { + let scope_tree = if let Some(body) = tcx.hir_maybe_body_owned_by(def_id) { let mut visitor = ScopeResolutionVisitor { tcx, scope_tree: ScopeTree::default(), diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 866787a457180..ea98beb955805 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -76,12 +76,12 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { | Node::Ctor(..) | Node::Field(_) => { let parent_id = tcx.hir_get_parent_item(hir_id); - Some(parent_id.to_def_id()) + Some(parent_id.def_id) } // FIXME(#43408) always enable this once `lazy_normalization` is // stable enough and does not need a feature gate anymore. Node::AnonConst(_) => { - let parent_did = tcx.parent(def_id.to_def_id()); + let parent_did = tcx.local_parent(def_id); debug!(?parent_did); let mut in_param_ty = false; @@ -175,7 +175,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { } Node::ConstBlock(_) | Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { - Some(tcx.typeck_root_def_id(def_id.to_def_id())) + Some(tcx.typeck_root_def_id_local(def_id)) } Node::OpaqueTy(&hir::OpaqueTy { origin: @@ -188,7 +188,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { } else { assert_matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn | DefKind::Fn); } - Some(fn_def_id.to_def_id()) + Some(fn_def_id) } Node::OpaqueTy(&hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias { parent, in_assoc_ty }, @@ -202,7 +202,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent); // Opaque types are always nested within another item, and // inherit the generics of the item. - Some(parent.to_def_id()) + Some(parent) } // All of these nodes have no parent from which to inherit generics. @@ -380,7 +380,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { own_params.iter().map(|param| (param.def_id, param.index)).collect(); ty::Generics { - parent: parent_def_id, + parent: parent_def_id.map(LocalDefId::to_def_id), parent_count, own_params, param_def_id_to_index, diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 82b7c578a1f25..1a9c2a21c7ef3 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -72,7 +72,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!(?bound_sig, ?liberated_sig); let parent_args = - GenericArgs::identity_for_item(tcx, tcx.typeck_root_def_id(expr_def_id.to_def_id())); + GenericArgs::identity_for_item(tcx, tcx.typeck_root_def_id_local(expr_def_id)); let tupled_upvars_ty = self.next_ty_var(expr_span); diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 734e5136f8806..6cfcdfe87480e 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -106,7 +106,7 @@ fn typeck_with_inspect<'tcx>( ) -> &'tcx ty::TypeckResults<'tcx> { // Closures' typeck results come from their outermost function, // as they are part of the same "inference environment". - let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(); + let typeck_root_def_id = tcx.typeck_root_def_id_local(def_id); if typeck_root_def_id != def_id { return tcx.typeck(typeck_root_def_id); } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index ff96f5044dc14..3572e3ff35cbe 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -1413,7 +1413,7 @@ rustc_queries! { /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; /// in the case of closures, this will be redirected to the enclosing function. - query region_scope_tree(def_id: DefId) -> &'tcx crate::middle::region::ScopeTree { + query region_scope_tree(def_id: LocalDefId) -> &'tcx crate::middle::region::ScopeTree { desc { "computing drop scopes for `{}`", tcx.def_path_str(def_id) } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 58a2edca8ecef..28688bcbfafc4 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -899,12 +899,8 @@ impl<'tcx> TyCtxt<'tcx> { pub fn has_typeck_results(self, def_id: LocalDefId) -> bool { // Closures' typeck results come from their outermost function, // as they are part of the same "inference environment". - let typeck_root_def_id = self.typeck_root_def_id(def_id.to_def_id()); - if typeck_root_def_id != def_id.to_def_id() { - return self.has_typeck_results(typeck_root_def_id.expect_local()); - } - - self.hir_node_by_def_id(def_id).body_id().is_some() + let root = self.typeck_root_def_id_local(def_id); + self.hir_node_by_def_id(root).body_id().is_some() } /// Expects a body and returns its codegen attributes. diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index e7c7d59248700..d0cbdff366dfa 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -650,6 +650,20 @@ impl<'tcx> TyCtxt<'tcx> { def_id } + /// Given the `LocalDefId`, returns the `LocalDefId` of the innermost item that + /// has its own type-checking context or "inference environment". + /// + /// For example, a closure has its own `LocalDefId`, but it is type-checked + /// with the containing item. Therefore, when we fetch the `typeck` of the closure, + /// for example, we really wind up fetching the `typeck` of the enclosing fn item. + pub fn typeck_root_def_id_local(self, def_id: LocalDefId) -> LocalDefId { + let mut def_id = def_id; + while self.is_typeck_child(def_id.to_def_id()) { + def_id = self.local_parent(def_id); + } + def_id + } + /// Given the `DefId` and args a closure, creates the type of /// `self` argument that the closure expects. For example, for a /// `Fn` closure, this would return a reference type `&T` where diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 4256ee45f8501..7e6f30378a490 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -816,8 +816,8 @@ impl<'tcx> ThirBuildCx<'tcx> { } hir::InlineAsmOperand::Const { ref anon_const } => { let ty = self.typeck_results.node_type(anon_const.hir_id); - let did = anon_const.def_id.to_def_id(); - let typeck_root_def_id = tcx.typeck_root_def_id(did); + let did = anon_const.def_id; + let typeck_root_def_id = tcx.typeck_root_def_id_local(did); let parent_args = tcx.erase_and_anonymize_regions( GenericArgs::identity_for_item(tcx, typeck_root_def_id), ); @@ -825,7 +825,7 @@ impl<'tcx> ThirBuildCx<'tcx> { InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty }) .args; - let uneval = mir::UnevaluatedConst::new(did, args); + let uneval = mir::UnevaluatedConst::new(did.to_def_id(), args); let value = mir::Const::Unevaluated(uneval, ty); InlineAsmOperand::Const { value, span: tcx.def_span(did) } } @@ -895,15 +895,15 @@ impl<'tcx> ThirBuildCx<'tcx> { hir::ExprKind::ConstBlock(ref anon_const) => { let ty = self.typeck_results.node_type(anon_const.hir_id); - let did = anon_const.def_id.to_def_id(); - let typeck_root_def_id = tcx.typeck_root_def_id(did); + let did = anon_const.def_id; + let typeck_root_def_id = tcx.typeck_root_def_id_local(did); let parent_args = tcx.erase_and_anonymize_regions(GenericArgs::identity_for_item( tcx, typeck_root_def_id, )); let args = InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty }).args; - ExprKind::ConstBlock { did, args } + ExprKind::ConstBlock { did: did.to_def_id(), args } } // Now comes the rote stuff: hir::ExprKind::Repeat(v, _) => { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 094aae499c4b4..9273c2103d442 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -526,7 +526,7 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & // We only need to borrowck non-synthetic MIR. let tainted_by_errors = if !tcx.is_synthetic_mir(def) { - tcx.mir_borrowck(tcx.typeck_root_def_id(def.to_def_id()).expect_local()).err() + tcx.mir_borrowck(tcx.typeck_root_def_id_local(def)).err() } else { None }; @@ -554,14 +554,14 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & // // We do this check here and not during `mir_promoted` because that may result // in borrowck cycles if WF requires looking into an opaque hidden type. - let root = tcx.typeck_root_def_id(def.to_def_id()); + let root = tcx.typeck_root_def_id_local(def); match tcx.def_kind(root) { DefKind::Fn | DefKind::AssocFn | DefKind::Static { .. } | DefKind::Const { .. } | DefKind::AssocConst { .. } => { - if let Err(guar) = tcx.ensure_result().check_well_formed(root.expect_local()) { + if let Err(guar) = tcx.ensure_result().check_well_formed(root) { body.tainted_by_errors = Some(guar); } } @@ -840,7 +840,7 @@ fn promoted_mir(tcx: TyCtxt<'_>, def: LocalDefId) -> &IndexVec(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Den } // Don't run unused pass for #[derive] - let parent = tcx.parent(tcx.typeck_root_def_id(def_id.to_def_id())); + let parent = tcx.local_parent(tcx.typeck_root_def_id_local(def_id)); if let DefKind::Impl { of_trait: true } = tcx.def_kind(parent) && find_attr!(tcx, parent, AutomaticallyDerived(..)) { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 8bd6730ab0eba..4698e054daf6e 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1615,7 +1615,7 @@ impl<'v> RootCollector<'_, 'v> { if (self.strategy == MonoItemCollectionStrategy::Eager || is_pub_fn_coroutine) && !self .tcx - .generics_of(self.tcx.typeck_root_def_id(def_id.to_def_id())) + .generics_of(self.tcx.typeck_root_def_id_local(def_id)) .requires_monomorphization(self.tcx) { let instance = match *self.tcx.type_of(def_id).instantiate_identity().kind() { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 7fcaea3a629f1..462f014a81f24 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -507,9 +507,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term, ty); - if let Some(body) = self.tcx.hir_maybe_body_owned_by( - self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(), - ) { + if let Some(body) = + self.tcx.hir_maybe_body_owned_by(self.tcx.typeck_root_def_id_local(body_def_id)) + { let expr = body.value; local_visitor.visit_expr(expr); } diff --git a/compiler/rustc_ty_utils/src/nested_bodies.rs b/compiler/rustc_ty_utils/src/nested_bodies.rs index 11dfbad7dbb7a..874c8161453b3 100644 --- a/compiler/rustc_ty_utils/src/nested_bodies.rs +++ b/compiler/rustc_ty_utils/src/nested_bodies.rs @@ -1,27 +1,26 @@ use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::Visitor; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt}; fn nested_bodies_within<'tcx>(tcx: TyCtxt<'tcx>, item: LocalDefId) -> &'tcx ty::List { let body = tcx.hir_body_owned_by(item); - let mut collector = - NestedBodiesVisitor { tcx, root_def_id: item.to_def_id(), nested_bodies: vec![] }; + let mut collector = NestedBodiesVisitor { tcx, root_def_id: item, nested_bodies: vec![] }; collector.visit_body(body); tcx.mk_local_def_ids(&collector.nested_bodies) } struct NestedBodiesVisitor<'tcx> { tcx: TyCtxt<'tcx>, - root_def_id: DefId, + root_def_id: LocalDefId, nested_bodies: Vec, } impl<'tcx> Visitor<'tcx> for NestedBodiesVisitor<'tcx> { fn visit_nested_body(&mut self, id: hir::BodyId) { let body_def_id = self.tcx.hir_body_owner_def_id(id); - if self.tcx.typeck_root_def_id(body_def_id.to_def_id()) == self.root_def_id { + if self.tcx.typeck_root_def_id_local(body_def_id) == self.root_def_id { // We visit nested bodies before adding the current body. This // means that nested bodies are always stored before their parent. let body = self.tcx.hir_body(id); diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index 4fa22ff0cdb5a..4ddf82773d872 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -179,7 +179,7 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { } fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool { - let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id()); + let scope_tree = cx.tcx.region_scope_tree(owner); if let Some(first_scope) = scope_tree.var_scope(first) && let Some(second_scope) = scope_tree.var_scope(second) { From 2bb9d70b1e48605006cde0c85a5bd833889271d7 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Fri, 27 Mar 2026 15:33:23 +0300 Subject: [PATCH 16/17] Use stable_crate_id more often --- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_symbol_mangling/src/legacy.rs | 4 +--- compiler/rustc_symbol_mangling/src/v0.rs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index e554da362f83e..8bf919dab8e79 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -727,7 +727,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { is_stub: false, }, extra_filename: tcx.sess.opts.cg.extra_filename.clone(), - stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(), + stable_crate_id: tcx.stable_crate_id(LOCAL_CRATE), required_panic_strategy: tcx.required_panic_strategy(LOCAL_CRATE), panic_in_drop_strategy: tcx.sess.opts.unstable_opts.panic_in_drop, edition: tcx.sess.edition(), diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index ea16231880e2c..54ecf277cd3df 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -155,9 +155,7 @@ fn get_symbol_hash<'tcx>( args.hash_stable(hcx, &mut hasher); if let Some(instantiating_crate) = instantiating_crate { - tcx.def_path_hash(instantiating_crate.as_def_id()) - .stable_crate_id() - .hash_stable(hcx, &mut hasher); + tcx.stable_crate_id(instantiating_crate).hash_stable(hcx, &mut hasher); } // We want to avoid accidental collision between different types of instances. diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 95cbb9e07ebb7..eff8cbef99541 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -841,7 +841,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.push("C"); if !self.is_exportable { - let stable_crate_id = self.tcx.def_path_hash(cnum.as_def_id()).stable_crate_id(); + let stable_crate_id = self.tcx.stable_crate_id(cnum); self.push_disambiguator(stable_crate_id.as_u64()); } let name = self.tcx.crate_name(cnum); From 62ec331a840ad7b69201bfabcf439ae2c6b0b466 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Fri, 27 Mar 2026 15:33:56 +0300 Subject: [PATCH 17/17] Use tcx.local_parent() more often --- compiler/rustc_hir_analysis/src/collect/predicates_of.rs | 2 +- compiler/rustc_passes/src/dead.rs | 4 ++-- .../src/error_reporting/infer/need_type_info.rs | 5 ++--- .../src/error_reporting/traits/suggestions.rs | 3 +-- src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 98999232a7435..8fd3d631962c8 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -107,7 +107,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen ); return ty::GenericPredicates { - parent: Some(tcx.parent(def_id.to_def_id())), + parent: Some(tcx.local_parent(def_id).to_def_id()), predicates: tcx.arena.alloc_from_iter(predicates), }; } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 30634c800e819..d5ef9ee9335c5 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -380,8 +380,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { /// for discussion). fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool { if let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind - && let impl_of = self.tcx.parent(impl_item.owner_id.to_def_id()) - && self.tcx.is_automatically_derived(impl_of) + && let impl_of = self.tcx.local_parent(impl_item.owner_id.def_id) + && self.tcx.is_automatically_derived(impl_of.to_def_id()) && let trait_ref = self.tcx.impl_trait_ref(impl_of).instantiate_identity() && find_attr!(self.tcx, trait_ref.def_id, RustcTrivialFieldReads) { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 462f014a81f24..46bc9bdee04b8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -516,9 +516,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let Some(InferSource { span, kind }) = local_visitor.infer_source else { let silence = if let DefKind::AssocFn = self.tcx.def_kind(body_def_id) - && let parent = self.tcx.parent(body_def_id.into()) - && self.tcx.is_automatically_derived(parent) - && let Some(parent) = parent.as_local() + && let parent = self.tcx.local_parent(body_def_id) + && self.tcx.is_automatically_derived(parent.to_def_id()) && let hir::Node::Item(item) = self.tcx.hir_node_by_def_id(parent) && let hir::ItemKind::Impl(imp) = item.kind && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = imp.self_ty.kind diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 4a6d5eb48f8f1..a16bbf20238c0 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1966,10 +1966,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut span = obligation.cause.span; if let DefKind::Closure = self.tcx.def_kind(obligation.cause.body_id) - && let parent = self.tcx.parent(obligation.cause.body_id.into()) + && let parent = self.tcx.local_parent(obligation.cause.body_id) && let DefKind::Fn | DefKind::AssocFn = self.tcx.def_kind(parent) && self.tcx.asyncness(parent).is_async() - && let Some(parent) = parent.as_local() && let Node::Item(hir::Item { kind: hir::ItemKind::Fn { sig: fn_sig, .. }, .. }) | Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(fn_sig, _), .. }) | Node::TraitItem(hir::TraitItem { diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index 8b0743c8d9d59..6c45964da0dab 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -332,7 +332,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { if let ImplItemImplKind::Trait { trait_item_def_id, .. } = impl_kind && let Ok(trait_item_id) = trait_item_def_id { - let impl_id = cx.tcx.parent(owner_id.into()); + let impl_id = cx.tcx.local_parent(owner_id.def_id); let trait_ref = cx.tcx.impl_trait_ref(impl_id).instantiate_identity(); ( trait_item_id,