Skip to content

Commit dda74fe

Browse files
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.**
2 parents 9e7b7d5 + 7bab769 commit dda74fe

File tree

21 files changed

+567
-360
lines changed

21 files changed

+567
-360
lines changed

compiler/rustc_driver/src/lib.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,7 @@ impl Callbacks for TimePassesCallbacks {
123123
fn config(&mut self, config: &mut interface::Config) {
124124
// If a --prints=... option has been given, we don't print the "total"
125125
// time because it will mess up the --prints output. See #64339.
126-
self.time_passes = config.opts.prints.is_empty()
127-
&& (config.opts.unstable_opts.time_passes || config.opts.unstable_opts.time);
126+
self.time_passes = config.opts.prints.is_empty() && config.opts.time_passes();
128127
config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath;
129128
}
130129
}
@@ -249,7 +248,7 @@ fn run_compiler(
249248
if sopts.describe_lints {
250249
let mut lint_store = rustc_lint::new_lint_store(
251250
sopts.unstable_opts.no_interleave_lints,
252-
compiler.session().unstable_options(),
251+
compiler.session().enable_internal_lints(),
253252
);
254253
let registered_lints =
255254
if let Some(register_lints) = compiler.register_lints() {

compiler/rustc_error_messages/locales/en-US/passes.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -256,3 +256,9 @@ passes-unused-duplicate = unused attribute
256256
passes-unused-multiple = multiple `{$name}` attributes
257257
.suggestion = remove this attribute
258258
.note = attribute also specified here
259+
260+
passes-rustc-lint-opt-ty = `#[rustc_lint_opt_ty]` should be applied to a struct
261+
.label = not a struct
262+
263+
passes-rustc-lint-opt-deny-field-access = `#[rustc_lint_opt_deny_field_access]` should be applied to a field
264+
.label = not a field

compiler/rustc_feature/src/builtin_attrs.rs

+6
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
619619
// Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints
620620
// to assist in changes to diagnostic APIs.
621621
rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
622+
// Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions`
623+
// types (as well as any others in future).
624+
rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
625+
// Used by the `rustc::bad_opt_access` lint on fields
626+
// types (as well as any others in future).
627+
rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE),
622628

623629
// ==========================================================================
624630
// Internal attributes, Const related:

compiler/rustc_interface/src/interface.rs

+2
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,8 @@ pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R
329329
})
330330
}
331331

332+
// JUSTIFICATION: before session exists, only config
333+
#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
332334
pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
333335
tracing::trace!("run_compiler");
334336
util::run_in_thread_pool_with_globals(

compiler/rustc_interface/src/passes.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ pub fn register_plugins<'a>(
210210

211211
let mut lint_store = rustc_lint::new_lint_store(
212212
sess.opts.unstable_opts.no_interleave_lints,
213-
sess.unstable_options(),
213+
sess.enable_internal_lints(),
214214
);
215215
register_lints(sess, &mut lint_store);
216216

compiler/rustc_interface/src/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
12
use crate::interface::parse_cfgspecs;
23

34
use rustc_data_structures::fx::FxHashSet;

compiler/rustc_interface/src/util.rs

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

compiler/rustc_lint/src/internal.rs

+35-14
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,6 @@ fn typeck_results_of_method_fn<'tcx>(
5151
cx: &LateContext<'tcx>,
5252
expr: &Expr<'_>,
5353
) -> Option<(Span, DefId, ty::subst::SubstsRef<'tcx>)> {
54-
// FIXME(rustdoc): Lints which use this function use typecheck results which can cause
55-
// `rustdoc` to error if there are resolution failures.
56-
//
57-
// As internal lints are currently always run if there are `unstable_options`, they are added
58-
// to the lint store of rustdoc. Internal lints are also not used via the `lint_mod` query.
59-
// Crate lints run outside of a query so rustdoc currently doesn't disable them.
60-
//
61-
// Instead of relying on this, either change crate lints to a query disabled by rustdoc, only
62-
// run internal lints if the user is explicitly opting in or figure out a different way to
63-
// avoid running lints for rustdoc.
64-
if cx.tcx.sess.opts.actually_rustdoc {
65-
return None;
66-
}
67-
6854
match expr.kind {
6955
ExprKind::MethodCall(segment, _, _)
7056
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
@@ -446,3 +432,38 @@ impl LateLintPass<'_> for Diagnostics {
446432
}
447433
}
448434
}
435+
436+
declare_tool_lint! {
437+
pub rustc::BAD_OPT_ACCESS,
438+
Deny,
439+
"prevent using options by field access when there is a wrapper function",
440+
report_in_external_macro: true
441+
}
442+
443+
declare_lint_pass!(BadOptAccess => [ BAD_OPT_ACCESS ]);
444+
445+
impl LateLintPass<'_> for BadOptAccess {
446+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
447+
let ExprKind::Field(base, target) = expr.kind else { return };
448+
let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return };
449+
// Skip types without `#[rustc_lint_opt_ty]` - only so that the rest of the lint can be
450+
// avoided.
451+
if !cx.tcx.has_attr(adt_def.did(), sym::rustc_lint_opt_ty) {
452+
return;
453+
}
454+
455+
for field in adt_def.all_fields() {
456+
if field.name == target.name &&
457+
let Some(attr) = cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access) &&
458+
let Some(items) = attr.meta_item_list() &&
459+
let Some(item) = items.first() &&
460+
let Some(literal) = item.literal() &&
461+
let ast::LitKind::Str(val, _) = literal.kind
462+
{
463+
cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, |lint| {
464+
lint.build(val.as_str()).emit(); }
465+
);
466+
}
467+
}
468+
}
469+
}

compiler/rustc_lint/src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -509,8 +509,14 @@ fn register_internals(store: &mut LintStore) {
509509
store.register_late_pass(|| Box::new(TyTyKind));
510510
store.register_lints(&Diagnostics::get_lints());
511511
store.register_late_pass(|| Box::new(Diagnostics));
512+
store.register_lints(&BadOptAccess::get_lints());
513+
store.register_late_pass(|| Box::new(BadOptAccess));
512514
store.register_lints(&PassByValue::get_lints());
513515
store.register_late_pass(|| Box::new(PassByValue));
516+
// FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and
517+
// `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and
518+
// these lints will trigger all of the time - change this once migration to diagnostic structs
519+
// and translation is completed
514520
store.register_group(
515521
false,
516522
"rustc::internal",
@@ -523,6 +529,7 @@ fn register_internals(store: &mut LintStore) {
523529
LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO),
524530
LintId::of(USAGE_OF_QUALIFIED_TY),
525531
LintId::of(EXISTING_DOC_KEYWORD),
532+
LintId::of(BAD_OPT_ACCESS),
526533
],
527534
);
528535
}

compiler/rustc_mir_transform/src/lower_slice_len.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub struct LowerSliceLenCalls;
1111

1212
impl<'tcx> MirPass<'tcx> for LowerSliceLenCalls {
1313
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
14-
sess.opts.mir_opt_level() > 0
14+
sess.mir_opt_level() > 0
1515
}
1616

1717
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {

compiler/rustc_mir_transform/src/reveal_all.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub struct RevealAll;
99

1010
impl<'tcx> MirPass<'tcx> for RevealAll {
1111
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
12-
sess.opts.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess)
12+
sess.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess)
1313
}
1414

1515
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {

compiler/rustc_passes/src/check_attr.rs

+33
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ impl CheckAttrVisitor<'_> {
121121
sym::rustc_lint_diagnostics => {
122122
self.check_rustc_lint_diagnostics(&attr, span, target)
123123
}
124+
sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(&attr, span, target),
125+
sym::rustc_lint_opt_deny_field_access => {
126+
self.check_rustc_lint_opt_deny_field_access(&attr, span, target)
127+
}
124128
sym::rustc_clean
125129
| sym::rustc_dirty
126130
| sym::rustc_if_this_changed
@@ -1382,6 +1386,35 @@ impl CheckAttrVisitor<'_> {
13821386
self.check_applied_to_fn_or_method(attr, span, target)
13831387
}
13841388

1389+
/// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct.
1390+
fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1391+
match target {
1392+
Target::Struct => true,
1393+
_ => {
1394+
self.tcx.sess.emit_err(errors::RustcLintOptTy { attr_span: attr.span, span });
1395+
false
1396+
}
1397+
}
1398+
}
1399+
1400+
/// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field.
1401+
fn check_rustc_lint_opt_deny_field_access(
1402+
&self,
1403+
attr: &Attribute,
1404+
span: Span,
1405+
target: Target,
1406+
) -> bool {
1407+
match target {
1408+
Target::Field => true,
1409+
_ => {
1410+
self.tcx
1411+
.sess
1412+
.emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span });
1413+
false
1414+
}
1415+
}
1416+
}
1417+
13851418
/// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
13861419
/// option is passed to the compiler.
13871420
fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {

compiler/rustc_passes/src/errors.rs

+18
Original file line numberDiff line numberDiff line change
@@ -625,3 +625,21 @@ pub struct UnusedMultiple {
625625
pub other: Span,
626626
pub name: Symbol,
627627
}
628+
629+
#[derive(SessionDiagnostic)]
630+
#[error(passes::rustc_lint_opt_ty)]
631+
pub struct RustcLintOptTy {
632+
#[primary_span]
633+
pub attr_span: Span,
634+
#[label]
635+
pub span: Span,
636+
}
637+
638+
#[derive(SessionDiagnostic)]
639+
#[error(passes::rustc_lint_opt_deny_field_access)]
640+
pub struct RustcLintOptDenyFieldAccess {
641+
#[primary_span]
642+
pub attr_span: Span,
643+
#[label]
644+
pub span: Span,
645+
}

compiler/rustc_session/src/config.rs

+4
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,8 @@ fn default_configuration(sess: &Session) -> CrateConfig {
948948
if sess.opts.debug_assertions {
949949
ret.insert((sym::debug_assertions, None));
950950
}
951+
// JUSTIFICATION: before wrapper fn is available
952+
#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
951953
if sess.opts.crate_types.contains(&CrateType::ProcMacro) {
952954
ret.insert((sym::proc_macro, None));
953955
}
@@ -2196,6 +2198,8 @@ fn parse_remap_path_prefix(
21962198
mapping
21972199
}
21982200

2201+
// JUSTIFICATION: before wrapper fn is available
2202+
#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
21992203
pub fn build_session_options(matches: &getopts::Matches) -> Options {
22002204
let color = parse_color(matches);
22012205

0 commit comments

Comments
 (0)