Rollup merge of #99710 - davidtwco:internal-lint-opts, r=lcnr

lint: add bad opt access internal lint

Prompted by [Zulip discussion](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/sess.2Ecrate_types.28.29.20vs.20sess.2Eopts.2Ecrate_types/near/290682847).

Some command-line options accessible through `sess.opts` are best accessed through wrapper functions on `Session`, `TyCtxt` or otherwise, rather than through field access on the option struct in the `Session`.

Adds a new lint which triggers on those options that should be accessed through a wrapper function so that this is prohibited. Options are annotated with a new attribute `rustc_lint_opt_deny_field_access` which can specify the error message (i.e. "use this other function instead") to be emitted.

A simpler alternative would be to simply rename the options in the option type so that it is clear they should not be used, however this doesn't prevent uses, just discourages them. Another alternative would be to make the option fields private, and adding accessor functions on the option types, however the wrapper functions sometimes rely on additional state from `Session` or `TyCtxt` which wouldn't be available in an function on the option type, so the accessor would simply make the field available and its use would be discouraged too.

**Leave a comment if there's an option I should add this to.**
This commit is contained in:
Guillaume Gomez 2022-07-27 17:55:05 +02:00 committed by GitHub
commit dda74fe8c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 523 additions and 316 deletions

View File

@ -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() {

View File

@ -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

View File

@ -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:

View File

@ -329,6 +329,8 @@ pub fn create_compiler_and_run<R>(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<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
tracing::trace!("run_compiler");
util::run_in_thread_pool_with_globals(

View File

@ -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);

View File

@ -1,3 +1,4 @@
#![cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
use crate::interface::parse_cfgspecs;
use rustc_data_structures::fx::FxHashSet;

View File

@ -559,6 +559,8 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C
// Only check command line flags if present. If no types are specified by
// command line, then reuse the empty `base` Vec to hold the types that
// will be found in crate attributes.
// JUSTIFICATION: before wrapper fn is available
#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
let mut base = session.opts.crate_types.clone();
if base.is_empty() {
base.extend(attr_types);

View File

@ -51,20 +51,6 @@ fn typeck_results_of_method_fn<'tcx>(
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(); }
);
}
}
}
}

View File

@ -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),
],
);
}

View File

@ -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>) {

View File

@ -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>) {

View File

@ -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 {

View File

@ -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,
}

View File

@ -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);

View File

@ -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<CrateType> [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<usize> [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<CodeModel> = (None, parse_code_model, [TRACKED],
"choose the code model to use (`rustc --print code-models` for details)"),
codegen_units: Option<usize> = (None, parse_opt_number, [UNTRACKED],
@ -1095,12 +1095,14 @@ options! {
"extra data to put in each output filename"),
force_frame_pointers: Option<bool> = (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<bool> = (None, parse_opt_bool, [TRACKED],
"force use of unwind tables"),
incremental: Option<String> = (None, parse_opt_string, [UNTRACKED],
"enable incremental compilation"),
inline_threshold: Option<u32> = (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<InstrumentCoverage> = (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<String> = (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<bool> = (None, parse_opt_bool, [TRACKED],
"keep dead code at link time (useful for code coverage) (default: no)"),
link_self_contained: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
@ -1127,6 +1130,7 @@ options! {
"generate build artifacts that are compatible with linker-based LTO"),
llvm_args: Vec<String> = (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<String> = (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<bool> = (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<PanicStrategy> = (None, parse_opt_panic_strategy, [TRACKED],
"panic strategy to compile crate with"),
passes: Vec<String> = (Vec::new(), parse_list, [TRACKED],
@ -1156,6 +1162,7 @@ options! {
"compile the program with profiling instrumentation"),
profile_use: Option<PathBuf> = (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<RelocModel> = (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<SplitDebuginfo> = (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<String> = (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<bool> = (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<InstrumentCoverage> = (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<MergeFunctions> = (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<usize> = (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<usize> = (None, parse_opt_number, [TRACKED],
@ -1431,6 +1446,7 @@ options! {
See #77382 and #74551."),
print_fuel: Option<String> = (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<String> = (None, parse_opt_string, [UNTRACKED],
@ -1505,6 +1521,7 @@ options! {
"exclude spans when debug-printing compiler state (default: no)"),
src_hash_algorithm: Option<SourceFileHashAlgorithm> = (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<SymbolManglingVersion> = (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<String> = (None, parse_opt_string, [UNTRACKED],
@ -1540,6 +1558,7 @@ options! {
"emit directionality isolation markers in translated diagnostics"),
tune_cpu: Option<String> = (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<bool> = (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<TlsModel> = (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<bool> = (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],

View File

@ -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,35 +583,28 @@ impl Session {
pub fn source_map(&self) -> &SourceMap {
self.parse_sess.source_map()
}
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
self.opts.time_passes()
}
pub fn instrument_mcount(&self) -> bool {
self.opts.unstable_opts.instrument_mcount
/// 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 time_llvm_passes(&self) -> bool {
self.opts.unstable_opts.time_llvm_passes
pub fn instrument_coverage(&self) -> bool {
self.opts.cg.instrument_coverage() != InstrumentCoverage::Off
}
pub fn meta_stats(&self) -> bool {
self.opts.unstable_opts.meta_stats
pub fn instrument_coverage_except_unused_generics(&self) -> bool {
self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedGenerics
}
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()
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.
@ -629,103 +622,9 @@ impl Session {
}
}
/// Calculates the flavor of LTO to use for this compilation.
pub fn lto(&self) -> config::Lto {
// If our target has codegen requirements ignore the command line
if self.target.requires_lto {
return config::Lto::Fat;
}
// If the user specified something, return that. If they only said `-C
// lto` and we've for whatever reason forced off ThinLTO via the CLI,
// then ensure we can't use a ThinLTO.
match self.opts.cg.lto {
config::LtoCli::Unspecified => {
// The compiler was invoked without the `-Clto` flag. Fall
// through to the default handling
}
config::LtoCli::No => {
// The user explicitly opted out of any kind of LTO
return config::Lto::No;
}
config::LtoCli::Yes | config::LtoCli::Fat | config::LtoCli::NoParam => {
// All of these mean fat LTO
return config::Lto::Fat;
}
config::LtoCli::Thin => {
return if self.opts.cli_forced_thinlto_off {
config::Lto::Fat
} else {
config::Lto::Thin
};
}
}
// Ok at this point the target doesn't require anything and the user
// hasn't asked for anything. Our next decision is whether or not
// we enable "auto" ThinLTO where we use multiple codegen units and
// then do ThinLTO over those codegen units. The logic below will
// either return `No` or `ThinLocal`.
// If processing command line options determined that we're incompatible
// with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option.
if self.opts.cli_forced_thinlto_off {
return config::Lto::No;
}
// If `-Z thinlto` specified process that, but note that this is mostly
// a deprecated option now that `-C lto=thin` exists.
if let Some(enabled) = self.opts.unstable_opts.thinlto {
if enabled {
return config::Lto::ThinLocal;
} else {
return config::Lto::No;
}
}
// If there's only one codegen unit and LTO isn't enabled then there's
// no need for ThinLTO so just return false.
if self.codegen_units() == 1 {
return config::Lto::No;
}
// Now we're in "defaults" territory. By default we enable ThinLTO for
// optimized compiles (anything greater than O0).
match self.opts.optimize {
config::OptLevel::No => config::Lto::No,
_ => config::Lto::ThinLocal,
}
}
/// Returns the panic strategy for this compile session. If the user explicitly selected one
/// using '-C panic', use that, otherwise use the panic strategy defined by the target.
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
} else {
let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly)
|| self.opts.output_types.contains_key(&OutputType::Bitcode)
// AddressSanitizer and MemorySanitizer use alloca name when reporting an issue.
|| self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY);
!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<CrateType>) -> bool {
@ -738,6 +637,8 @@ impl Session {
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)
@ -752,18 +653,6 @@ impl Session {
}
}
pub fn relocation_model(&self) -> RelocModel {
self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model)
}
pub fn code_model(&self) -> Option<CodeModel> {
self.opts.cg.code_model.or(self.target.code_model)
}
pub fn tls_model(&self) -> TlsModel {
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!(
@ -772,49 +661,10 @@ impl Session {
)
}
pub fn split_debuginfo(&self) -> SplitDebuginfo {
self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo)
}
pub fn stack_protector(&self) -> StackProtector {
if self.target.options.supports_stack_protector {
self.opts.unstable_opts.stack_protector
} else {
StackProtector::None
}
}
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.
//
// Unwind tables are needed when compiling with `-C panic=unwind`, but
// LLVM won't omit unwind tables unless the function is also marked as
// `nounwind`, so users are allowed to disable `uwtable` emission.
// Historically rustc always emits `uwtable` attributes by default, so
// even they can be disabled, they're still emitted by default.
//
// On some targets (including windows), however, exceptions include
// other events such as illegal instructions, segfaults, etc. This means
// that on Windows we end up still needing unwind tables even if the `-C
// panic=abort` flag is passed.
//
// You can also find more info on why Windows needs unwind tables in:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
//
// If a target requires unwind tables, then they must be emitted.
// Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>`
// value, if it is provided, or disable them, if not.
self.target.requires_uwtable
|| self.opts.cg.force_unwind_tables.unwrap_or(
self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable,
)
}
pub fn generate_proc_macro_decls_symbol(&self, stable_crate_id: StableCrateId) -> String {
format!("__rustc_proc_macro_decls_{:08x}__", stable_crate_id.to_u64())
}
@ -960,6 +810,280 @@ impl Session {
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<Item = &'a Attribute> {
attrs.iter().filter(move |attr| attr.has_name(name))
}
pub fn first_attr_value_str_by_name(
&self,
attrs: &[Attribute],
name: Symbol,
) -> Option<Symbol> {
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 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
.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.
pub fn lto(&self) -> config::Lto {
// If our target has codegen requirements ignore the command line
if self.target.requires_lto {
return config::Lto::Fat;
}
// If the user specified something, return that. If they only said `-C
// lto` and we've for whatever reason forced off ThinLTO via the CLI,
// then ensure we can't use a ThinLTO.
match self.opts.cg.lto {
config::LtoCli::Unspecified => {
// The compiler was invoked without the `-Clto` flag. Fall
// through to the default handling
}
config::LtoCli::No => {
// The user explicitly opted out of any kind of LTO
return config::Lto::No;
}
config::LtoCli::Yes | config::LtoCli::Fat | config::LtoCli::NoParam => {
// All of these mean fat LTO
return config::Lto::Fat;
}
config::LtoCli::Thin => {
return if self.opts.cli_forced_thinlto_off {
config::Lto::Fat
} else {
config::Lto::Thin
};
}
}
// Ok at this point the target doesn't require anything and the user
// hasn't asked for anything. Our next decision is whether or not
// we enable "auto" ThinLTO where we use multiple codegen units and
// then do ThinLTO over those codegen units. The logic below will
// either return `No` or `ThinLocal`.
// If processing command line options determined that we're incompatible
// with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option.
if self.opts.cli_forced_thinlto_off {
return config::Lto::No;
}
// If `-Z thinlto` specified process that, but note that this is mostly
// a deprecated option now that `-C lto=thin` exists.
if let Some(enabled) = self.opts.unstable_opts.thinlto {
if enabled {
return config::Lto::ThinLocal;
} else {
return config::Lto::No;
}
}
// If there's only one codegen unit and LTO isn't enabled then there's
// no need for ThinLTO so just return false.
if self.codegen_units() == 1 {
return config::Lto::No;
}
// Now we're in "defaults" territory. By default we enable ThinLTO for
// optimized compiles (anything greater than O0).
match self.opts.optimize {
config::OptLevel::No => config::Lto::No,
_ => config::Lto::ThinLocal,
}
}
/// Returns the panic strategy for this compile session. If the user explicitly selected one
/// using '-C panic', use that, otherwise use the panic strategy defined by the target.
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
} else {
let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly)
|| self.opts.output_types.contains_key(&OutputType::Bitcode)
// AddressSanitizer and MemorySanitizer use alloca name when reporting an issue.
|| self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY);
!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 overflow_checks(&self) -> bool {
self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions)
}
pub fn relocation_model(&self) -> RelocModel {
self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model)
}
pub fn code_model(&self) -> Option<CodeModel> {
self.opts.cg.code_model.or(self.target.code_model)
}
pub fn tls_model(&self) -> TlsModel {
self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model)
}
pub fn split_debuginfo(&self) -> SplitDebuginfo {
self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo)
}
pub fn stack_protector(&self) -> StackProtector {
if self.target.options.supports_stack_protector {
self.opts.unstable_opts.stack_protector
} else {
StackProtector::None
}
}
pub fn must_emit_unwind_tables(&self) -> bool {
// This is used to control the emission of the `uwtable` attribute on
// LLVM functions.
//
// Unwind tables are needed when compiling with `-C panic=unwind`, but
// LLVM won't omit unwind tables unless the function is also marked as
// `nounwind`, so users are allowed to disable `uwtable` emission.
// Historically rustc always emits `uwtable` attributes by default, so
// even they can be disabled, they're still emitted by default.
//
// On some targets (including windows), however, exceptions include
// other events such as illegal instructions, segfaults, etc. This means
// that on Windows we end up still needing unwind tables even if the `-C
// panic=abort` flag is passed.
//
// You can also find more info on why Windows needs unwind tables in:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
//
// If a target requires unwind tables, then they must be emitted.
// Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>`
// value, if it is provided, or disable them, if not.
self.target.requires_uwtable
|| self.opts.cg.force_unwind_tables.unwrap_or(
self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable,
)
}
/// 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<Item = &'a Attribute> {
attrs.iter().filter(move |attr| attr.has_name(name))
}
pub fn first_attr_value_str_by_name(
&self,
attrs: &[Attribute],
name: Symbol,
) -> Option<Symbol> {
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<dyn Write + Send>),
}
// 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<PathBuf>,
@ -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

View File

@ -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,

View File

@ -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);

View File

@ -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!
}

View File

@ -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

View File

@ -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();