Skip to content

Commit

Permalink
Improve diagnostics for the use of unstable library features
Browse files Browse the repository at this point in the history
- only emits one error/lint (instead of one per missing feature) per usage of unstable and
  body-unstable items
- only emits one future-name-collision lint (instead of one per missing feature) for unstable trait
  items
- makes diagnostics for unstable, soft-unstable, const-unstable, and body-unstable library features
  translatable, using common subdiagnostic structs.
- adds notes with features, reasons, and issue links to const-unstability errors
- adds notes with issue links to soft_unstable lints
- on nightly, adds `#![feature]` crate attr help to soft_unstable lints
- on nightly, adds compiler-upgrade-suggestion notes to const-unstable and soft_unstable diagnostics
  • Loading branch information
dianne committed Oct 17, 2024
1 parent 8f284de commit e6015a6
Show file tree
Hide file tree
Showing 39 changed files with 505 additions and 318 deletions.
4 changes: 2 additions & 2 deletions compiler/rustc_const_eval/src/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
let feature_gate_declared = gate_declared(feature);
let implied_gate_declared = implied_by.is_some_and(gate_declared);
if !feature_gate_declared && !implied_gate_declared {
bad_gates.push(feature);
bad_gates.push(gate.into());
continue;
}

Expand All @@ -735,7 +735,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
continue;
}

bad_gates.push(feature);
bad_gates.push(gate.into());
}
if !bad_gates.is_empty() {
self.check_op(ops::FnCallUnstable(callee, bad_gates));
Expand Down
44 changes: 26 additions & 18 deletions compiler/rustc_const_eval/src/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
use rustc_middle::middle::stability;
use rustc_middle::mir::CallSource;
use rustc_middle::span_bug;
use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
Expand Down Expand Up @@ -298,28 +299,35 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
///
/// Contains the names of the features that would allow the use of this function.
#[derive(Debug)]
pub(crate) struct FnCallUnstable(pub DefId, pub Vec<Symbol>);
pub(crate) struct FnCallUnstable(pub DefId, pub Vec<stability::EvalDenial>);

impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
let FnCallUnstable(def_id, features) = self;

let mut err = ccx
.dcx()
.create_err(errors::UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) });

// FIXME: make this translatable
#[allow(rustc::untranslatable_diagnostic)]
if ccx.is_const_stable_const_fn() {
err.help(fluent_generated::const_eval_const_stable);
} else if ccx.tcx.sess.is_nightly_build() && !features.is_empty() {
err.help(format!(
"add `#![feature({})]` to the crate attributes to enable",
features.iter().map(Symbol::as_str).intersperse(", ").collect::<String>(),
));
}
let FnCallUnstable(def_id, denials) = self;

err
let in_const_stable_context = ccx.is_const_stable_const_fn();
// Only report unstable features when they're present.
let (features, info) = if denials.is_empty() {
(None, vec![])
} else {
let (features, info) = stability::unstable_notes(&denials);
(Some(features), info)
};
// Only suggest adding `#![feature]` if it could help.
let nightly_subdiags = if in_const_stable_context || denials.is_empty() {
vec![]
} else {
stability::unstable_nightly_subdiags(&ccx.tcx.sess, &denials, None)
};

ccx.dcx().create_err(errors::UnstableConstFn {
span,
def_path: ccx.tcx.def_path_str(def_id),
in_const_stable_context,
features,
info,
nightly_subdiags,
})
}
}

Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_const_eval/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ pub(crate) struct UnstableConstFn {
#[primary_span]
pub span: Span,
pub def_path: String,
#[help(const_eval_const_stable)]
pub in_const_stable_context: bool,
#[subdiagnostic]
pub features: Option<rustc_middle::error::UnstableLibraryFeatureNote>,
#[subdiagnostic]
pub info: Vec<rustc_middle::error::UnstableLibraryFeatureInfo>,
#[subdiagnostic]
pub nightly_subdiags: Vec<rustc_session::errors::NightlyFeatureDiagnostic>,
}

#[derive(Diagnostic)]
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,6 @@ hir_analysis_missing_trait_item_suggestion = implement the missing item: `{$snip
hir_analysis_missing_trait_item_unstable = not all trait items implemented, missing: `{$missing_item_name}`
.note = default implementation of `{$missing_item_name}` is unstable
.some_note = use of unstable library feature `{$feature}`: {$reason}
.none_note = use of unstable library feature `{$feature}`
hir_analysis_missing_type_params =
the type {$parameterCount ->
Expand Down
9 changes: 1 addition & 8 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -933,14 +933,7 @@ fn check_impl_items_against_trait<'tcx>(
let full_impl_span = tcx.hir().span_with_body(tcx.local_def_id_to_hir_id(impl_id));
match tcx.eval_default_body_stability(trait_item_id, full_impl_span) {
EvalResult::Deny { denials, .. } => {
for denial in denials {
default_body_is_unstable(
tcx,
full_impl_span,
trait_item_id,
denial.unstability,
);
}
default_body_is_unstable(tcx, full_impl_span, trait_item_id, &denials);
}

// Unmarked default bodies are considered stable (at least for now).
Expand Down
40 changes: 11 additions & 29 deletions compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ use rustc_index::bit_set::BitSet;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{self, TyCtxtInferExt as _};
use rustc_infer::traits::ObligationCause;
use rustc_middle::middle::stability;
use rustc_middle::query::Providers;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, GenericArgs, GenericArgsRef, Ty, TyCtxt};
Expand Down Expand Up @@ -293,41 +294,22 @@ fn default_body_is_unstable(
tcx: TyCtxt<'_>,
impl_span: Span,
item_did: DefId,
unstability: rustc_attr::Unstability,
denials: &[stability::EvalDenial],
) {
let rustc_attr::Unstability { feature, reason, issue, .. } = unstability;
let missing_item_name = tcx.associated_item(item_did).name;
let (mut some_note, mut none_note, mut reason_str) = (false, false, String::new());
match reason.to_opt_reason() {
Some(r) => {
some_note = true;
reason_str = r.to_string();
}
None => none_note = true,
};

let mut err = tcx.dcx().create_err(errors::MissingTraitItemUnstable {
span: impl_span,
some_note,
none_note,
missing_item_name,
feature,
reason: reason_str,
});

let inject_span = item_did
.as_local()
.and_then(|id| tcx.crate_level_attribute_injection_span(tcx.local_def_id_to_hir_id(id)));
rustc_session::parse::add_feature_diagnostics_for_issue(
&mut err,
&tcx.sess,
feature,
rustc_feature::GateIssue::Library(issue),
false,
inject_span,
);
let (features, info) = stability::unstable_notes(denials);
let nightly_subdiags = stability::unstable_nightly_subdiags(&tcx.sess, denials, inject_span);

err.emit();
tcx.dcx().emit_err(errors::MissingTraitItemUnstable {
span: impl_span,
missing_item_name,
features,
info,
nightly_subdiags,
});
}

/// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions.
Expand Down
12 changes: 6 additions & 6 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -977,13 +977,13 @@ pub(crate) struct MissingOneOfTraitItem {
pub(crate) struct MissingTraitItemUnstable {
#[primary_span]
pub span: Span,
#[note(hir_analysis_some_note)]
pub some_note: bool,
#[note(hir_analysis_none_note)]
pub none_note: bool,
pub missing_item_name: Symbol,
pub feature: Symbol,
pub reason: String,
#[subdiagnostic]
pub features: rustc_middle::error::UnstableLibraryFeatureNote,
#[subdiagnostic]
pub info: Vec<rustc_middle::error::UnstableLibraryFeatureInfo>,
#[subdiagnostic]
pub nightly_subdiags: Vec<rustc_session::errors::NightlyFeatureDiagnostic>,
}

#[derive(Diagnostic)]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_typeck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#![feature(array_windows)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(never_type)]
#![feature(try_blocks)]
Expand Down
14 changes: 6 additions & 8 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1329,10 +1329,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
if let stability::EvalResult::Deny { denials, .. } =
self.tcx.eval_stability(candidate.item.def_id, None, self.span, None)
{
uc.push((
candidate.clone(),
denials.iter().map(|d| d.unstability.feature).collect(),
));
uc.push((candidate.clone(), denials.iter().map(|d| d.feature).collect()));
return false;
}
true
Expand Down Expand Up @@ -1431,10 +1428,11 @@ impl<'tcx> Pick<'tcx> {
tcx.disabled_nightly_features(
lint,
Some(scope_expr_id),
self.unstable_candidates.iter().flat_map(|(candidate, features)| {
features.iter().map(|feature| {
(format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature)
})
self.unstable_candidates.iter().map(|(candidate, features)| {
(
format!(" `{}`", tcx.def_path_str(candidate.item.def_id)),
features.iter().map(Symbol::as_str).intersperse(", ").collect::<String>(),
)
}),
);
});
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_lint/src/context/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,12 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
BuiltinLintDiag::MacroRuleNeverUsed(n, name) => {
lints::MacroRuleNeverUsed { n: n + 1, name }.decorate_lint(diag);
}
BuiltinLintDiag::UnstableFeature(msg) => {
lints::UnstableFeature { msg }.decorate_lint(diag);
BuiltinLintDiag::SoftUnstableMacro { features } => {
let denials = features
.iter()
.map(|&(feature, reason, issue)| stability::EvalDenial { feature, reason, issue })
.collect::<Vec<_>>();
stability::soft_unstable(sess, &denials, vec![]).decorate_lint(diag);
}
BuiltinLintDiag::AvoidUsingIntelSyntax => {
lints::AvoidIntelSyntax.decorate_lint(diag);
Expand Down
10 changes: 0 additions & 10 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2404,16 +2404,6 @@ pub(crate) struct MacroRuleNeverUsed {
pub name: Symbol,
}

pub(crate) struct UnstableFeature {
pub msg: DiagMessage,
}

impl<'a> LintDiagnostic<'a, ()> for UnstableFeature {
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
diag.primary_message(self.msg);
}
}

#[derive(LintDiagnostic)]
#[diag(lint_avoid_intel_syntax)]
pub(crate) struct AvoidIntelSyntax;
Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
#![warn(unreachable_pub)]
// tidy-alphabetical-end

use std::num::NonZero;

use rustc_ast::node_id::NodeId;
use rustc_ast::{AttrId, Attribute};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::stable_hasher::{
HashStable, StableCompare, StableHasher, ToStableHashKey,
};
use rustc_error_messages::{DiagMessage, MultiSpan};
use rustc_error_messages::MultiSpan;
use rustc_hir::def::Namespace;
use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind};
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
Expand Down Expand Up @@ -715,7 +717,10 @@ pub enum BuiltinLintDiag {
MacroIsPrivate(Ident),
UnusedMacroDefinition(Symbol),
MacroRuleNeverUsed(usize, Symbol),
UnstableFeature(DiagMessage),
SoftUnstableMacro {
/// The name, optional reason, and issue number for each disabled unstable feature used.
features: Vec<(Symbol, Option<Symbol>, Option<NonZero<u32>>)>,
},
AvoidUsingIntelSyntax,
AvoidUsingAttSyntax,
IncompleteInclude,
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_middle/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,25 @@ middle_type_length_limit = reached the type-length limit while instantiating `{$
middle_unknown_layout =
the type `{$ty}` has an unknown layout
middle_unstable_library_feature = use of unstable library {$count ->
[one] feature {$features}{$single_feature_has_reason ->
[true] : {$reason_for_single_feature}
*[false] {""}
}
*[other] features {$features}
}
middle_unstable_library_feature_issue =
see issue #{$issue} <https://github.com/rust-lang/rust/issues/{$issue}> for more information{$show_feature ->
[true] {" "}about `{$feature}`
*[false] {""}
}
middle_unstable_library_feature_reason = reason for `{$feature}`: {$reason}
middle_unstable_library_feature_suggestion_for_allocator_api =
consider wrapping the inner types in tuple
middle_values_too_big =
values of the type `{$ty}` are too big for the target architecture
middle_written_to_path = the full type name has been written to '{$path}'
Loading

0 comments on commit e6015a6

Please sign in to comment.