diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index f5b059793cf4b..53ae913f94f12 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -123,8 +123,7 @@ impl Callbacks for TimePassesCallbacks { fn config(&mut self, config: &mut interface::Config) { // If a --prints=... option has been given, we don't print the "total" // time because it will mess up the --prints output. See #64339. - self.time_passes = config.opts.prints.is_empty() - && (config.opts.unstable_opts.time_passes || config.opts.unstable_opts.time); + self.time_passes = config.opts.prints.is_empty() && config.opts.time_passes(); config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath; } } @@ -249,7 +248,7 @@ fn run_compiler( if sopts.describe_lints { let mut lint_store = rustc_lint::new_lint_store( sopts.unstable_opts.no_interleave_lints, - compiler.session().unstable_options(), + compiler.session().enable_internal_lints(), ); let registered_lints = if let Some(register_lints) = compiler.register_lints() { diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl index f95e33cd16a7d..b17eb9c2d260e 100644 --- a/compiler/rustc_error_messages/locales/en-US/passes.ftl +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -256,3 +256,9 @@ passes-unused-duplicate = unused attribute passes-unused-multiple = multiple `{$name}` attributes .suggestion = remove this attribute .note = attribute also specified here + +passes-rustc-lint-opt-ty = `#[rustc_lint_opt_ty]` should be applied to a struct + .label = not a struct + +passes-rustc-lint-opt-deny-field-access = `#[rustc_lint_opt_deny_field_access]` should be applied to a field + .label = not a field diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 7b540e67aab3d..0e73d8fd7f600 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -619,6 +619,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints // to assist in changes to diagnostic APIs. rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions` + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint on fields + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE), // ========================================================================== // Internal attributes, Const related: diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 6c7ddb4531ef8..94f81b6607798 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -329,6 +329,8 @@ pub fn create_compiler_and_run(config: Config, f: impl FnOnce(&Compiler) -> R }) } +// JUSTIFICATION: before session exists, only config +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { tracing::trace!("run_compiler"); util::run_in_thread_pool_with_globals( diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 334a595a88ae6..8f0835917861a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -210,7 +210,7 @@ pub fn register_plugins<'a>( let mut lint_store = rustc_lint::new_lint_store( sess.opts.unstable_opts.no_interleave_lints, - sess.unstable_options(), + sess.enable_internal_lints(), ); register_lints(sess, &mut lint_store); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0a0eb99cd9266..21d9eaccf67e7 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] use crate::interface::parse_cfgspecs; use rustc_data_structures::fx::FxHashSet; diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 97856ecf22c66..4c64e679b9571 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -559,6 +559,8 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec( cx: &LateContext<'tcx>, expr: &Expr<'_>, ) -> Option<(Span, DefId, ty::subst::SubstsRef<'tcx>)> { - // FIXME(rustdoc): Lints which use this function use typecheck results which can cause - // `rustdoc` to error if there are resolution failures. - // - // As internal lints are currently always run if there are `unstable_options`, they are added - // to the lint store of rustdoc. Internal lints are also not used via the `lint_mod` query. - // Crate lints run outside of a query so rustdoc currently doesn't disable them. - // - // Instead of relying on this, either change crate lints to a query disabled by rustdoc, only - // run internal lints if the user is explicitly opting in or figure out a different way to - // avoid running lints for rustdoc. - if cx.tcx.sess.opts.actually_rustdoc { - return None; - } - match expr.kind { ExprKind::MethodCall(segment, _, _) if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => @@ -446,3 +432,38 @@ impl LateLintPass<'_> for Diagnostics { } } } + +declare_tool_lint! { + pub rustc::BAD_OPT_ACCESS, + Deny, + "prevent using options by field access when there is a wrapper function", + report_in_external_macro: true +} + +declare_lint_pass!(BadOptAccess => [ BAD_OPT_ACCESS ]); + +impl LateLintPass<'_> for BadOptAccess { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let ExprKind::Field(base, target) = expr.kind else { return }; + let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return }; + // Skip types without `#[rustc_lint_opt_ty]` - only so that the rest of the lint can be + // avoided. + if !cx.tcx.has_attr(adt_def.did(), sym::rustc_lint_opt_ty) { + return; + } + + for field in adt_def.all_fields() { + if field.name == target.name && + let Some(attr) = cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access) && + let Some(items) = attr.meta_item_list() && + let Some(item) = items.first() && + let Some(literal) = item.literal() && + let ast::LitKind::Str(val, _) = literal.kind + { + cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, |lint| { + lint.build(val.as_str()).emit(); } + ); + } + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 8726d36498bed..7b0702dad75de 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -509,8 +509,14 @@ fn register_internals(store: &mut LintStore) { store.register_late_pass(|| Box::new(TyTyKind)); store.register_lints(&Diagnostics::get_lints()); store.register_late_pass(|| Box::new(Diagnostics)); + store.register_lints(&BadOptAccess::get_lints()); + store.register_late_pass(|| Box::new(BadOptAccess)); store.register_lints(&PassByValue::get_lints()); store.register_late_pass(|| Box::new(PassByValue)); + // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and + // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and + // these lints will trigger all of the time - change this once migration to diagnostic structs + // and translation is completed store.register_group( false, "rustc::internal", @@ -523,6 +529,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), LintId::of(USAGE_OF_QUALIFIED_TY), LintId::of(EXISTING_DOC_KEYWORD), + LintId::of(BAD_OPT_ACCESS), ], ); } diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 47848cfa497f3..2f02d00ec9fb0 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -11,7 +11,7 @@ pub struct LowerSliceLenCalls; impl<'tcx> MirPass<'tcx> for LowerSliceLenCalls { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.opts.mir_opt_level() > 0 + sess.mir_opt_level() > 0 } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/reveal_all.rs b/compiler/rustc_mir_transform/src/reveal_all.rs index 8ea550fa123b6..4919ad40098cb 100644 --- a/compiler/rustc_mir_transform/src/reveal_all.rs +++ b/compiler/rustc_mir_transform/src/reveal_all.rs @@ -9,7 +9,7 @@ pub struct RevealAll; impl<'tcx> MirPass<'tcx> for RevealAll { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.opts.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess) + sess.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f3ccbbb56792d..fde12b9eee6b9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -121,6 +121,10 @@ impl CheckAttrVisitor<'_> { sym::rustc_lint_diagnostics => { self.check_rustc_lint_diagnostics(&attr, span, target) } + sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(&attr, span, target), + sym::rustc_lint_opt_deny_field_access => { + self.check_rustc_lint_opt_deny_field_access(&attr, span, target) + } sym::rustc_clean | sym::rustc_dirty | sym::rustc_if_this_changed @@ -1382,6 +1386,35 @@ impl CheckAttrVisitor<'_> { self.check_applied_to_fn_or_method(attr, span, target) } + /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct. + fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool { + match target { + Target::Struct => true, + _ => { + self.tcx.sess.emit_err(errors::RustcLintOptTy { attr_span: attr.span, span }); + false + } + } + } + + /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field. + fn check_rustc_lint_opt_deny_field_access( + &self, + attr: &Attribute, + span: Span, + target: Target, + ) -> bool { + match target { + Target::Field => true, + _ => { + self.tcx + .sess + .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span }); + false + } + } + } + /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph /// option is passed to the compiler. fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 0d4317f6b8881..5feb0e2956b74 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -625,3 +625,21 @@ pub struct UnusedMultiple { pub other: Span, pub name: Symbol, } + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_lint_opt_ty)] +pub struct RustcLintOptTy { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_lint_opt_deny_field_access)] +pub struct RustcLintOptDenyFieldAccess { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 55307b9cebb70..fe9ef6045415e 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -948,6 +948,8 @@ fn default_configuration(sess: &Session) -> CrateConfig { if sess.opts.debug_assertions { ret.insert((sym::debug_assertions, None)); } + // JUSTIFICATION: before wrapper fn is available + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] if sess.opts.crate_types.contains(&CrateType::ProcMacro) { ret.insert((sym::proc_macro, None)); } @@ -2196,6 +2198,8 @@ fn parse_remap_path_prefix( mapping } +// JUSTIFICATION: before wrapper fn is available +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn build_session_options(matches: &getopts::Matches) -> Options { let color = parse_color(matches); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 28e2e0db89a1f..501997679f4bf 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -102,28 +102,6 @@ macro_rules! top_level_options { ); } -impl Options { - pub fn mir_opt_level(&self) -> usize { - self.unstable_opts - .mir_opt_level - .unwrap_or_else(|| if self.optimize != OptLevel::No { 2 } else { 1 }) - } - - pub fn instrument_coverage(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) != InstrumentCoverage::Off - } - - pub fn instrument_coverage_except_unused_generics(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) - == InstrumentCoverage::ExceptUnusedGenerics - } - - pub fn instrument_coverage_except_unused_functions(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) - == InstrumentCoverage::ExceptUnusedFunctions - } -} - top_level_options!( /// The top-level command-line options struct. /// @@ -149,9 +127,11 @@ top_level_options!( /// `CodegenOptions`, think about how it influences incremental compilation. If in /// doubt, specify `[TRACKED]`, which is always "correct" but might lead to /// unnecessary re-compilation. + #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)] pub struct Options { /// The crate config requested for the session, which may be combined /// with additional crate configurations during the compile process. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::crate_types` instead of this field"))] crate_types: Vec [TRACKED], optimize: OptLevel [TRACKED], /// Include the `debug_assertions` flag in dependency tracking, since it @@ -198,7 +178,9 @@ top_level_options!( /// what rustc was invoked with, but massaged a bit to agree with /// commands like `--emit llvm-ir` which they're often incompatible with /// if we otherwise use the defaults of rustc. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::codegen_units` instead of this field"))] cli_forced_codegen_units: Option [UNTRACKED], + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] cli_forced_thinlto_off: bool [UNTRACKED], /// Remap source path prefixes in all output (messages, object files, debug, etc.). @@ -249,11 +231,12 @@ macro_rules! options { ),* ,) => ( #[derive(Clone)] - pub struct $struct_name { $(pub $opt: $t),* } + #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)] + pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* } impl Default for $struct_name { fn default() -> $struct_name { - $struct_name { $( $( #[$attr] )* $opt: $init),* } + $struct_name { $($opt: $init),* } } } @@ -297,6 +280,22 @@ macro_rules! options { ) } +impl Options { + // JUSTIFICATION: defn of the suggested wrapper fn + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + pub fn time_passes(&self) -> bool { + self.unstable_opts.time_passes || self.unstable_opts.time + } +} + +impl CodegenOptions { + // JUSTIFICATION: defn of the suggested wrapper fn + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + pub fn instrument_coverage(&self) -> InstrumentCoverage { + self.instrument_coverage.unwrap_or(InstrumentCoverage::Off) + } +} + // Sometimes different options need to build a common structure. // That structure can be kept in one of the options' fields, the others become dummy. macro_rules! redirect_field { @@ -1076,6 +1075,7 @@ options! { ar: String = (String::new(), parse_string, [UNTRACKED], "this option is deprecated and does nothing"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field"))] code_model: Option = (None, parse_code_model, [TRACKED], "choose the code model to use (`rustc --print code-models` for details)"), codegen_units: Option = (None, parse_opt_number, [UNTRACKED], @@ -1095,12 +1095,14 @@ options! { "extra data to put in each output filename"), force_frame_pointers: Option = (None, parse_opt_bool, [TRACKED], "force use of the frame pointers"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::must_emit_unwind_tables` instead of this field"))] force_unwind_tables: Option = (None, parse_opt_bool, [TRACKED], "force use of unwind tables"), incremental: Option = (None, parse_opt_string, [UNTRACKED], "enable incremental compilation"), inline_threshold: Option = (None, parse_opt_number, [TRACKED], "set the threshold for inlining a function"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))] instrument_coverage: Option = (None, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`); \ @@ -1113,6 +1115,7 @@ options! { "a single extra argument to append to the linker invocation (can be used several times)"), link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], "extra arguments to append to the linker invocation (space separated)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::link_dead_code` instead of this field"))] link_dead_code: Option = (None, parse_opt_bool, [TRACKED], "keep dead code at link time (useful for code coverage) (default: no)"), link_self_contained: Option = (None, parse_opt_bool, [UNTRACKED], @@ -1127,6 +1130,7 @@ options! { "generate build artifacts that are compatible with linker-based LTO"), llvm_args: Vec = (Vec::new(), parse_list, [TRACKED], "a list of arguments to pass to LLVM (space separated)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] lto: LtoCli = (LtoCli::Unspecified, parse_lto, [TRACKED], "perform LLVM link-time optimizations"), metadata: Vec = (Vec::new(), parse_list, [TRACKED], @@ -1143,8 +1147,10 @@ options! { "disable LLVM's SLP vectorization pass"), opt_level: String = ("0".to_string(), parse_string, [TRACKED], "optimization level (0-3, s, or z; default: 0)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::overflow_checks` instead of this field"))] overflow_checks: Option = (None, parse_opt_bool, [TRACKED], "use overflow checks for integer arithmetic"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::panic_strategy` instead of this field"))] panic: Option = (None, parse_opt_panic_strategy, [TRACKED], "panic strategy to compile crate with"), passes: Vec = (Vec::new(), parse_list, [TRACKED], @@ -1156,6 +1162,7 @@ options! { "compile the program with profiling instrumentation"), profile_use: Option = (None, parse_opt_pathbuf, [TRACKED], "use the given `.profdata` file for profile-guided optimization"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::relocation_model` instead of this field"))] relocation_model: Option = (None, parse_relocation_model, [TRACKED], "control generation of position-independent code (PIC) \ (`rustc --print relocation-models` for details)"), @@ -1167,6 +1174,7 @@ options! { "save all temporary output files during compilation (default: no)"), soft_float: bool = (false, parse_bool, [TRACKED], "use soft float ABI (*eabihf targets only) (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::split_debuginfo` instead of this field"))] split_debuginfo: Option = (None, parse_split_debuginfo, [TRACKED], "how to handle split-debuginfo, a platform-specific option"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], @@ -1202,11 +1210,13 @@ options! { "encode MIR of all functions into the crate metadata (default: no)"), assume_incomplete_release: bool = (false, parse_bool, [TRACKED], "make cfg(version) treat the current version as incomplete (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::asm_comments` instead of this field"))] asm_comments: bool = (false, parse_bool, [TRACKED], "generate comments into the assembly (may change behavior) (default: no)"), assert_incr_state: Option = (None, parse_opt_string, [UNTRACKED], "assert that the incremental cache is in given state: \ either `loaded` or `not-loaded`."), + #[cfg_attr(not(bootstrap), 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)"), @@ -1284,6 +1294,7 @@ options! { "emit the bc module with thin LTO info (default: yes)"), export_executable_symbols: bool = (false, parse_bool, [TRACKED], "export symbols from executables, as if they were dynamic libraries"), + #[cfg_attr(not(bootstrap), 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)"), @@ -1326,6 +1337,7 @@ options! { "control whether `#[inline]` functions are in all CGUs"), input_stats: bool = (false, parse_bool, [UNTRACKED], "gather statistics about the input (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))] instrument_coverage: Option = (None, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`); \ @@ -1334,6 +1346,7 @@ options! { `=except-unused-generics` `=except-unused-functions` `=off` (default)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_mcount` instead of this field"))] instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], @@ -1356,6 +1369,7 @@ options! { 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"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::meta_stats` instead of this field"))] meta_stats: bool = (false, parse_bool, [UNTRACKED], "gather metadata statistics (default: no)"), mir_emit_retag: bool = (false, parse_bool, [TRACKED], @@ -1365,6 +1379,7 @@ options! { "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \ enabled, overriding all other checks. Passes that are not specified are enabled or \ disabled by other flags as usual."), + #[cfg_attr(not(bootstrap), 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)"), move_size_limit: Option = (None, parse_opt_number, [TRACKED], @@ -1431,6 +1446,7 @@ options! { See #77382 and #74551."), print_fuel: Option = (None, parse_opt_string, [TRACKED], "make rustc print the total optimization fuel used by a crate"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::print_llvm_passes` instead of this field"))] print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "print the LLVM optimization passes being run (default: no)"), print_mono_items: Option = (None, parse_opt_string, [UNTRACKED], @@ -1505,6 +1521,7 @@ options! { "exclude spans when debug-printing compiler state (default: no)"), src_hash_algorithm: Option = (None, parse_src_file_hash, [TRACKED], "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), + #[cfg_attr(not(bootstrap), 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)"), strict_init_checks: bool = (false, parse_bool, [TRACKED], @@ -1525,6 +1542,7 @@ options! { symbol_mangling_version: Option = (None, parse_symbol_mangling_version, [TRACKED], "which mangling version to use for symbol names ('legacy' (default) or 'v0')"), + #[cfg_attr(not(bootstrap), 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], @@ -1540,6 +1558,7 @@ options! { "emit directionality isolation markers in translated diagnostics"), tune_cpu: Option = (None, parse_opt_string, [TRACKED], "select processor to schedule for (`rustc --print target-cpus` for details)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] thinlto: Option = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), thir_unsafeck: bool = (false, parse_bool, [TRACKED], @@ -1548,14 +1567,19 @@ options! { /// 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. + #[cfg_attr(not(bootstrap), 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"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))] time: bool = (false, parse_bool, [UNTRACKED], "measure time of rustc processes (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_llvm_passes` instead of this field"))] time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each LLVM pass (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))] time_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each rustc pass (default: no)"), + #[cfg_attr(not(bootstrap), 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], @@ -1590,14 +1614,17 @@ options! { "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. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::unstable_options` instead of this field"))] unstable_options: bool = (false, parse_bool, [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"), validate_mir: bool = (false, parse_bool, [UNTRACKED], "validate MIR after each transformation"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verbose` instead of this field"))] verbose: bool = (false, parse_bool, [UNTRACKED], "in general, enable more debug printouts (default: no)"), + #[cfg_attr(not(bootstrap), 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], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 854cad79a2040..9669287b3f370 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1,7 +1,7 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; -use crate::config::{self, CrateType, OutputType, SwitchWithOptPath}; +use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath}; use crate::parse::{add_feature_diagnostics, ParseSess}; use crate::search_paths::{PathKind, SearchPath}; use crate::{filesearch, lint}; @@ -583,50 +583,356 @@ impl Session { pub fn source_map(&self) -> &SourceMap { self.parse_sess.source_map() } + + pub fn time_passes(&self) -> bool { + self.opts.time_passes() + } + + /// Returns `true` if internal lints should be added to the lint store - i.e. if + /// `-Zunstable-options` is provided and this isn't rustdoc (internal lints can trigger errors + /// to be emitted under rustdoc). + pub fn enable_internal_lints(&self) -> bool { + self.unstable_options() && !self.opts.actually_rustdoc + } + + pub fn instrument_coverage(&self) -> bool { + self.opts.cg.instrument_coverage() != InstrumentCoverage::Off + } + + pub fn instrument_coverage_except_unused_generics(&self) -> bool { + self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedGenerics + } + + pub fn instrument_coverage_except_unused_functions(&self) -> bool { + self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedFunctions + } + + /// Gets the features enabled for the current compilation session. + /// DO NOT USE THIS METHOD if there is a TyCtxt available, as it circumvents + /// dependency tracking. Use tcx.features() instead. + #[inline] + pub fn features_untracked(&self) -> &rustc_feature::Features { + self.features.get().unwrap() + } + + pub fn init_features(&self, features: rustc_feature::Features) { + match self.features.set(features) { + Ok(()) => {} + Err(_) => panic!("`features` was initialized twice"), + } + } + + pub fn is_sanitizer_cfi_enabled(&self) -> bool { + self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI) + } + + /// Check whether this compile session and crate type use static crt. + pub fn crt_static(&self, crate_type: Option) -> bool { + if !self.target.crt_static_respected { + // If the target does not opt in to crt-static support, use its default. + return self.target.crt_static_default; + } + + let requested_features = self.opts.cg.target_feature.split(','); + let found_negative = requested_features.clone().any(|r| r == "-crt-static"); + let found_positive = requested_features.clone().any(|r| r == "+crt-static"); + + // JUSTIFICATION: necessary use of crate_types directly (see FIXME below) + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + if found_positive || found_negative { + found_positive + } else if crate_type == Some(CrateType::ProcMacro) + || crate_type == None && self.opts.crate_types.contains(&CrateType::ProcMacro) + { + // FIXME: When crate_type is not available, + // we use compiler options to determine the crate_type. + // We can't check `#![crate_type = "proc-macro"]` here. + false + } else { + self.target.crt_static_default + } + } + + pub fn is_wasi_reactor(&self) -> bool { + self.target.options.os == "wasi" + && matches!( + self.opts.unstable_opts.wasi_exec_model, + Some(config::WasiExecModel::Reactor) + ) + } + + pub fn target_can_use_split_dwarf(&self) -> bool { + !self.target.is_like_windows && !self.target.is_like_osx + } + + pub fn generate_proc_macro_decls_symbol(&self, stable_crate_id: StableCrateId) -> String { + format!("__rustc_proc_macro_decls_{:08x}__", stable_crate_id.to_u64()) + } + + pub fn target_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { + filesearch::FileSearch::new( + &self.sysroot, + self.opts.target_triple.triple(), + &self.opts.search_paths, + &self.target_tlib_path, + kind, + ) + } + pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { + filesearch::FileSearch::new( + &self.sysroot, + config::host_triple(), + &self.opts.search_paths, + &self.host_tlib_path, + kind, + ) + } + + /// Returns a list of directories where target-specific tool binaries are located. + pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec { + let rustlib_path = rustc_target::target_rustlib_path(&self.sysroot, &config::host_triple()); + let p = PathBuf::from_iter([ + Path::new(&self.sysroot), + Path::new(&rustlib_path), + Path::new("bin"), + ]); + if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] } + } + + pub fn init_incr_comp_session( + &self, + session_dir: PathBuf, + lock_file: flock::Lock, + load_dep_graph: bool, + ) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + if let IncrCompSession::NotInitialized = *incr_comp_session { + } else { + panic!("Trying to initialize IncrCompSession `{:?}`", *incr_comp_session) + } + + *incr_comp_session = + IncrCompSession::Active { session_directory: session_dir, lock_file, load_dep_graph }; + } + + pub fn finalize_incr_comp_session(&self, new_directory_path: PathBuf) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + if let IncrCompSession::Active { .. } = *incr_comp_session { + } else { + panic!("trying to finalize `IncrCompSession` `{:?}`", *incr_comp_session); + } + + // Note: this will also drop the lock file, thus unlocking the directory. + *incr_comp_session = IncrCompSession::Finalized { session_directory: new_directory_path }; + } + + pub fn mark_incr_comp_session_as_invalid(&self) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + let session_directory = match *incr_comp_session { + IncrCompSession::Active { ref session_directory, .. } => session_directory.clone(), + IncrCompSession::InvalidBecauseOfErrors { .. } => return, + _ => panic!("trying to invalidate `IncrCompSession` `{:?}`", *incr_comp_session), + }; + + // Note: this will also drop the lock file, thus unlocking the directory. + *incr_comp_session = IncrCompSession::InvalidBecauseOfErrors { session_directory }; + } + + pub fn incr_comp_session_dir(&self) -> cell::Ref<'_, PathBuf> { + let incr_comp_session = self.incr_comp_session.borrow(); + cell::Ref::map(incr_comp_session, |incr_comp_session| match *incr_comp_session { + IncrCompSession::NotInitialized => panic!( + "trying to get session directory from `IncrCompSession`: {:?}", + *incr_comp_session, + ), + IncrCompSession::Active { ref session_directory, .. } + | IncrCompSession::Finalized { ref session_directory } + | IncrCompSession::InvalidBecauseOfErrors { ref session_directory } => { + session_directory + } + }) + } + + pub fn incr_comp_session_dir_opt(&self) -> Option> { + self.opts.incremental.as_ref().map(|_| self.incr_comp_session_dir()) + } + + pub fn print_perf_stats(&self) { + eprintln!( + "Total time spent computing symbol hashes: {}", + duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock()) + ); + eprintln!( + "Total queries canonicalized: {}", + self.perf_stats.queries_canonicalized.load(Ordering::Relaxed) + ); + eprintln!( + "normalize_generic_arg_after_erasing_regions: {}", + self.perf_stats.normalize_generic_arg_after_erasing_regions.load(Ordering::Relaxed) + ); + eprintln!( + "normalize_projection_ty: {}", + self.perf_stats.normalize_projection_ty.load(Ordering::Relaxed) + ); + } + + /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. + /// This expends fuel if applicable, and records fuel if applicable. + pub fn consider_optimizing String>(&self, crate_name: &str, msg: T) -> bool { + let mut ret = true; + if let Some((ref c, _)) = self.opts.unstable_opts.fuel { + if c == crate_name { + assert_eq!(self.threads(), 1); + let mut fuel = self.optimization_fuel.lock(); + ret = fuel.remaining != 0; + if fuel.remaining == 0 && !fuel.out_of_fuel { + if self.diagnostic().can_emit_warnings() { + // We only call `msg` in case we can actually emit warnings. + // Otherwise, this could cause a `delay_good_path_bug` to + // trigger (issue #79546). + self.warn(&format!("optimization-fuel-exhausted: {}", msg())); + } + fuel.out_of_fuel = true; + } else if fuel.remaining > 0 { + fuel.remaining -= 1; + } + } + } + if let Some(ref c) = self.opts.unstable_opts.print_fuel { + if c == crate_name { + assert_eq!(self.threads(), 1); + self.print_fuel.fetch_add(1, SeqCst); + } + } + ret + } + + pub fn rust_2015(&self) -> bool { + self.edition() == Edition::Edition2015 + } + + /// Are we allowed to use features from the Rust 2018 edition? + pub fn rust_2018(&self) -> bool { + self.edition() >= Edition::Edition2018 + } + + /// Are we allowed to use features from the Rust 2021 edition? + pub fn rust_2021(&self) -> bool { + self.edition() >= Edition::Edition2021 + } + + /// Are we allowed to use features from the Rust 2024 edition? + pub fn rust_2024(&self) -> bool { + self.edition() >= Edition::Edition2024 + } + + /// Returns `true` if we cannot skip the PLT for shared library calls. + pub fn needs_plt(&self) -> bool { + // Check if the current target usually needs PLT to be enabled. + // The user can use the command line flag to override it. + let needs_plt = self.target.needs_plt; + + let dbg_opts = &self.opts.unstable_opts; + + let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level); + + // Only enable this optimization by default if full relro is also enabled. + // In this case, lazy binding was already unavailable, so nothing is lost. + // This also ensures `-Wl,-z,now` is supported by the linker. + let full_relro = RelroLevel::Full == relro_level; + + // If user didn't explicitly forced us to use / skip the PLT, + // then try to skip it where possible. + dbg_opts.plt.unwrap_or(needs_plt || !full_relro) + } + + /// Checks if LLVM lifetime markers should be emitted. + pub fn emit_lifetime_markers(&self) -> bool { + self.opts.optimize != config::OptLevel::No + // AddressSanitizer uses lifetimes to detect use after scope bugs. + // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. + // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. + || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) + } + + pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { + [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] + .iter() + .any(|kind| attr.has_name(*kind)) + } + + pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool { + attrs.iter().any(|item| item.has_name(name)) + } + + pub fn find_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> Option<&'a Attribute> { + attrs.iter().find(|attr| attr.has_name(name)) + } + + pub fn filter_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> impl Iterator { + attrs.iter().filter(move |attr| attr.has_name(name)) + } + + pub fn first_attr_value_str_by_name( + &self, + attrs: &[Attribute], + name: Symbol, + ) -> Option { + attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str()) + } +} + +// JUSTIFICATION: defn of the suggested wrapper fns +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] +impl Session { pub fn verbose(&self) -> bool { self.opts.unstable_opts.verbose } - pub fn time_passes(&self) -> bool { - self.opts.unstable_opts.time_passes || self.opts.unstable_opts.time - } + pub fn instrument_mcount(&self) -> bool { self.opts.unstable_opts.instrument_mcount } + pub fn time_llvm_passes(&self) -> bool { self.opts.unstable_opts.time_llvm_passes } + pub fn meta_stats(&self) -> bool { self.opts.unstable_opts.meta_stats } + pub fn asm_comments(&self) -> bool { self.opts.unstable_opts.asm_comments } + pub fn verify_llvm_ir(&self) -> bool { self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() } + pub fn print_llvm_passes(&self) -> bool { self.opts.unstable_opts.print_llvm_passes } + pub fn binary_dep_depinfo(&self) -> bool { self.opts.unstable_opts.binary_dep_depinfo } - pub fn mir_opt_level(&self) -> usize { - self.opts.mir_opt_level() - } - - /// Gets the features enabled for the current compilation session. - /// DO NOT USE THIS METHOD if there is a TyCtxt available, as it circumvents - /// dependency tracking. Use tcx.features() instead. - #[inline] - pub fn features_untracked(&self) -> &rustc_feature::Features { - self.features.get().unwrap() - } - pub fn init_features(&self, features: rustc_feature::Features) { - match self.features.set(features) { - Ok(()) => {} - Err(_) => panic!("`features` was initialized twice"), - } + pub fn mir_opt_level(&self) -> usize { + self.opts + .unstable_opts + .mir_opt_level + .unwrap_or_else(|| if self.opts.optimize != OptLevel::No { 2 } else { 1 }) } /// Calculates the flavor of LTO to use for this compilation. @@ -702,6 +1008,7 @@ impl Session { pub fn panic_strategy(&self) -> PanicStrategy { self.opts.cg.panic.unwrap_or(self.target.panic_strategy) } + pub fn fewer_names(&self) -> bool { if let Some(fewer_names) = self.opts.unstable_opts.fewer_names { fewer_names @@ -713,43 +1020,17 @@ impl Session { !more_names } } - - pub fn unstable_options(&self) -> bool { - self.opts.unstable_opts.unstable_options - } - pub fn is_nightly_build(&self) -> bool { - self.opts.unstable_features.is_nightly_build() - } - pub fn is_sanitizer_cfi_enabled(&self) -> bool { - self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI) - } - pub fn overflow_checks(&self) -> bool { - self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions) - } - - /// Check whether this compile session and crate type use static crt. - pub fn crt_static(&self, crate_type: Option) -> bool { - if !self.target.crt_static_respected { - // If the target does not opt in to crt-static support, use its default. - return self.target.crt_static_default; - } - - let requested_features = self.opts.cg.target_feature.split(','); - let found_negative = requested_features.clone().any(|r| r == "-crt-static"); - let found_positive = requested_features.clone().any(|r| r == "+crt-static"); - - if found_positive || found_negative { - found_positive - } else if crate_type == Some(CrateType::ProcMacro) - || crate_type == None && self.opts.crate_types.contains(&CrateType::ProcMacro) - { - // FIXME: When crate_type is not available, - // we use compiler options to determine the crate_type. - // We can't check `#![crate_type = "proc-macro"]` here. - false - } else { - self.target.crt_static_default - } + + pub fn unstable_options(&self) -> bool { + self.opts.unstable_opts.unstable_options + } + + pub fn is_nightly_build(&self) -> bool { + self.opts.unstable_features.is_nightly_build() + } + + pub fn overflow_checks(&self) -> bool { + self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions) } pub fn relocation_model(&self) -> RelocModel { @@ -764,14 +1045,6 @@ impl Session { self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model) } - pub fn is_wasi_reactor(&self) -> bool { - self.target.options.os == "wasi" - && matches!( - self.opts.unstable_opts.wasi_exec_model, - Some(config::WasiExecModel::Reactor) - ) - } - pub fn split_debuginfo(&self) -> SplitDebuginfo { self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo) } @@ -784,10 +1057,6 @@ impl Session { } } - pub fn target_can_use_split_dwarf(&self) -> bool { - !self.target.is_like_windows && !self.target.is_like_osx - } - pub fn must_emit_unwind_tables(&self) -> bool { // This is used to control the emission of the `uwtable` attribute on // LLVM functions. @@ -815,151 +1084,6 @@ impl Session { ) } - pub fn generate_proc_macro_decls_symbol(&self, stable_crate_id: StableCrateId) -> String { - format!("__rustc_proc_macro_decls_{:08x}__", stable_crate_id.to_u64()) - } - - pub fn target_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { - filesearch::FileSearch::new( - &self.sysroot, - self.opts.target_triple.triple(), - &self.opts.search_paths, - &self.target_tlib_path, - kind, - ) - } - pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { - filesearch::FileSearch::new( - &self.sysroot, - config::host_triple(), - &self.opts.search_paths, - &self.host_tlib_path, - kind, - ) - } - - /// Returns a list of directories where target-specific tool binaries are located. - pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec { - let rustlib_path = rustc_target::target_rustlib_path(&self.sysroot, &config::host_triple()); - let p = PathBuf::from_iter([ - Path::new(&self.sysroot), - Path::new(&rustlib_path), - Path::new("bin"), - ]); - if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] } - } - - pub fn init_incr_comp_session( - &self, - session_dir: PathBuf, - lock_file: flock::Lock, - load_dep_graph: bool, - ) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - if let IncrCompSession::NotInitialized = *incr_comp_session { - } else { - panic!("Trying to initialize IncrCompSession `{:?}`", *incr_comp_session) - } - - *incr_comp_session = - IncrCompSession::Active { session_directory: session_dir, lock_file, load_dep_graph }; - } - - pub fn finalize_incr_comp_session(&self, new_directory_path: PathBuf) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - if let IncrCompSession::Active { .. } = *incr_comp_session { - } else { - panic!("trying to finalize `IncrCompSession` `{:?}`", *incr_comp_session); - } - - // Note: this will also drop the lock file, thus unlocking the directory. - *incr_comp_session = IncrCompSession::Finalized { session_directory: new_directory_path }; - } - - pub fn mark_incr_comp_session_as_invalid(&self) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - let session_directory = match *incr_comp_session { - IncrCompSession::Active { ref session_directory, .. } => session_directory.clone(), - IncrCompSession::InvalidBecauseOfErrors { .. } => return, - _ => panic!("trying to invalidate `IncrCompSession` `{:?}`", *incr_comp_session), - }; - - // Note: this will also drop the lock file, thus unlocking the directory. - *incr_comp_session = IncrCompSession::InvalidBecauseOfErrors { session_directory }; - } - - pub fn incr_comp_session_dir(&self) -> cell::Ref<'_, PathBuf> { - let incr_comp_session = self.incr_comp_session.borrow(); - cell::Ref::map(incr_comp_session, |incr_comp_session| match *incr_comp_session { - IncrCompSession::NotInitialized => panic!( - "trying to get session directory from `IncrCompSession`: {:?}", - *incr_comp_session, - ), - IncrCompSession::Active { ref session_directory, .. } - | IncrCompSession::Finalized { ref session_directory } - | IncrCompSession::InvalidBecauseOfErrors { ref session_directory } => { - session_directory - } - }) - } - - pub fn incr_comp_session_dir_opt(&self) -> Option> { - self.opts.incremental.as_ref().map(|_| self.incr_comp_session_dir()) - } - - pub fn print_perf_stats(&self) { - eprintln!( - "Total time spent computing symbol hashes: {}", - duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock()) - ); - eprintln!( - "Total queries canonicalized: {}", - self.perf_stats.queries_canonicalized.load(Ordering::Relaxed) - ); - eprintln!( - "normalize_generic_arg_after_erasing_regions: {}", - self.perf_stats.normalize_generic_arg_after_erasing_regions.load(Ordering::Relaxed) - ); - eprintln!( - "normalize_projection_ty: {}", - self.perf_stats.normalize_projection_ty.load(Ordering::Relaxed) - ); - } - - /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. - /// This expends fuel if applicable, and records fuel if applicable. - pub fn consider_optimizing String>(&self, crate_name: &str, msg: T) -> bool { - let mut ret = true; - if let Some((ref c, _)) = self.opts.unstable_opts.fuel { - if c == crate_name { - assert_eq!(self.threads(), 1); - let mut fuel = self.optimization_fuel.lock(); - ret = fuel.remaining != 0; - if fuel.remaining == 0 && !fuel.out_of_fuel { - if self.diagnostic().can_emit_warnings() { - // We only call `msg` in case we can actually emit warnings. - // Otherwise, this could cause a `delay_good_path_bug` to - // trigger (issue #79546). - self.warn(&format!("optimization-fuel-exhausted: {}", msg())); - } - fuel.out_of_fuel = true; - } else if fuel.remaining > 0 { - fuel.remaining -= 1; - } - } - } - if let Some(ref c) = self.opts.unstable_opts.print_fuel { - if c == crate_name { - assert_eq!(self.threads(), 1); - self.print_fuel.fetch_add(1, SeqCst); - } - } - ret - } - /// Returns the number of query threads that should be used for this /// compilation pub fn threads(&self) -> usize { @@ -1040,109 +1164,17 @@ impl Session { self.opts.unstable_opts.teach && self.diagnostic().must_teach(code) } - pub fn rust_2015(&self) -> bool { - self.opts.edition == Edition::Edition2015 - } - - /// Are we allowed to use features from the Rust 2018 edition? - pub fn rust_2018(&self) -> bool { - self.opts.edition >= Edition::Edition2018 - } - - /// Are we allowed to use features from the Rust 2021 edition? - pub fn rust_2021(&self) -> bool { - self.opts.edition >= Edition::Edition2021 - } - - /// Are we allowed to use features from the Rust 2024 edition? - pub fn rust_2024(&self) -> bool { - self.opts.edition >= Edition::Edition2024 - } - pub fn edition(&self) -> Edition { self.opts.edition } - /// Returns `true` if we cannot skip the PLT for shared library calls. - pub fn needs_plt(&self) -> bool { - // Check if the current target usually needs PLT to be enabled. - // The user can use the command line flag to override it. - let needs_plt = self.target.needs_plt; - - let dbg_opts = &self.opts.unstable_opts; - - let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level); - - // Only enable this optimization by default if full relro is also enabled. - // In this case, lazy binding was already unavailable, so nothing is lost. - // This also ensures `-Wl,-z,now` is supported by the linker. - let full_relro = RelroLevel::Full == relro_level; - - // If user didn't explicitly forced us to use / skip the PLT, - // then try to skip it where possible. - dbg_opts.plt.unwrap_or(needs_plt || !full_relro) - } - - /// Checks if LLVM lifetime markers should be emitted. - pub fn emit_lifetime_markers(&self) -> bool { - self.opts.optimize != config::OptLevel::No - // AddressSanitizer uses lifetimes to detect use after scope bugs. - // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. - // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. - || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) - } - pub fn link_dead_code(&self) -> bool { self.opts.cg.link_dead_code.unwrap_or(false) } - - pub fn instrument_coverage(&self) -> bool { - self.opts.instrument_coverage() - } - - pub fn instrument_coverage_except_unused_generics(&self) -> bool { - self.opts.instrument_coverage_except_unused_generics() - } - - pub fn instrument_coverage_except_unused_functions(&self) -> bool { - self.opts.instrument_coverage_except_unused_functions() - } - - pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { - [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] - .iter() - .any(|kind| attr.has_name(*kind)) - } - - pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool { - attrs.iter().any(|item| item.has_name(name)) - } - - pub fn find_by_name<'a>( - &'a self, - attrs: &'a [Attribute], - name: Symbol, - ) -> Option<&'a Attribute> { - attrs.iter().find(|attr| attr.has_name(name)) - } - - pub fn filter_by_name<'a>( - &'a self, - attrs: &'a [Attribute], - name: Symbol, - ) -> impl Iterator { - attrs.iter().filter(move |attr| attr.has_name(name)) - } - - pub fn first_attr_value_str_by_name( - &self, - attrs: &[Attribute], - name: Symbol, - ) -> Option { - attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str()) - } } +// JUSTIFICATION: part of session construction +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] fn default_emitter( sopts: &config::Options, registry: rustc_errors::registry::Registry, @@ -1227,6 +1259,8 @@ pub enum DiagnosticOutput { Raw(Box), } +// JUSTIFICATION: literally session construction +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn build_session( sopts: config::Options, local_crate_source_file: Option, @@ -1348,11 +1382,8 @@ pub fn build_session( CguReuseTracker::new_disabled() }; - let prof = SelfProfilerRef::new( - self_profiler, - sopts.unstable_opts.time_passes || sopts.unstable_opts.time, - sopts.unstable_opts.time_passes, - ); + let prof = + SelfProfilerRef::new(self_profiler, sopts.time_passes(), sopts.unstable_opts.time_passes); let ctfe_backtrace = Lock::new(match env::var("RUSTC_CTFE_BACKTRACE") { Ok(ref val) if val == "immediate" => CtfeBacktrace::Immediate, @@ -1401,8 +1432,12 @@ pub fn build_session( sess } -// If it is useful to have a Session available already for validating a -// commandline argument, you can do so here. +/// Validate command line arguments with a `Session`. +/// +/// If it is useful to have a Session available already for validating a commandline argument, you +/// can do so here. +// JUSTIFICATION: needs to access args to validate them +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] fn validate_commandline_args_with_session_available(sess: &Session) { // Since we don't know if code in an rlib will be linked to statically or // dynamically downstream, rustc generates `__imp_` symbols that help linkers diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c75b6772487f9..060e7a7b90aee 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1238,6 +1238,8 @@ symbols! { rustc_layout_scalar_valid_range_start, rustc_legacy_const_generics, rustc_lint_diagnostics, + rustc_lint_opt_deny_field_access, + rustc_lint_opt_ty, rustc_lint_query_instability, rustc_macro_transparency, rustc_main, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 7429f2b6ab148..3a96884d45d9f 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -788,7 +788,7 @@ fn main_options(options: config::Options) -> MainResult { if sess.opts.describe_lints { let mut lint_store = rustc_lint::new_lint_store( sess.opts.unstable_opts.no_interleave_lints, - sess.unstable_options(), + sess.enable_internal_lints(), ); let registered_lints = if let Some(register_lints) = compiler.register_lints() { register_lints(sess, &mut lint_store); diff --git a/src/test/ui-fulldeps/internal-lints/bad_opt_access.rs b/src/test/ui-fulldeps/internal-lints/bad_opt_access.rs new file mode 100644 index 0000000000000..d6bd6945e150a --- /dev/null +++ b/src/test/ui-fulldeps/internal-lints/bad_opt_access.rs @@ -0,0 +1,22 @@ +// compile-flags: -Z unstable-options + +// Test that accessing command line options by field access triggers a lint for those fields +// that have wrapper functions which should be used. + +#![crate_type = "lib"] +#![feature(rustc_private)] +#![deny(rustc::bad_opt_access)] + +extern crate rustc_session; +use rustc_session::Session; + +pub fn access_bad_option(sess: Session) { + let _ = sess.opts.cg.split_debuginfo; + //~^ ERROR use `Session::split_debuginfo` instead of this field + + let _ = sess.opts.crate_types; + //~^ ERROR use `Session::crate_types` instead of this field + + let _ = sess.opts.crate_name; + // okay! +} diff --git a/src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr b/src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr new file mode 100644 index 0000000000000..e4145bff8bee9 --- /dev/null +++ b/src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr @@ -0,0 +1,20 @@ +error: use `Session::split_debuginfo` instead of this field + --> $DIR/bad_opt_access.rs:14:13 + | +LL | let _ = sess.opts.cg.split_debuginfo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/bad_opt_access.rs:8:9 + | +LL | #![deny(rustc::bad_opt_access)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: use `Session::crate_types` instead of this field + --> $DIR/bad_opt_access.rs:17:13 + | +LL | let _ = sess.opts.crate_types; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index c219c7de830ef..c1ec2bd5bd665 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -94,6 +94,8 @@ struct ClippyCallbacks { } impl rustc_driver::Callbacks for ClippyCallbacks { + // JUSTIFICATION: necessary in clippy driver to set `mir_opt_level` + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] fn config(&mut self, config: &mut interface::Config) { let previous = config.register_lints.take(); let clippy_args_var = self.clippy_args_var.take();