Skip to content

Commit 9a80171

Browse files
committed
Allow multiple unstable features in diagnostics
1 parent 9573d7b commit 9a80171

File tree

10 files changed

+53
-43
lines changed

10 files changed

+53
-43
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3682,6 +3682,7 @@ dependencies = [
36823682
name = "rustc_feature"
36833683
version = "0.0.0"
36843684
dependencies = [
3685+
"either",
36853686
"rustc_data_structures",
36863687
"rustc_span",
36873688
]

compiler/rustc_ast_passes/src/feature_gate.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use rustc_ast as ast;
22
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
33
use rustc_ast::{NodeId, PatKind, attr, token};
4-
use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features, GateIssue};
4+
use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features};
55
use rustc_session::Session;
6-
use rustc_session::parse::{feature_err, feature_err_issue, feature_warn};
6+
use rustc_session::parse::{feature_err, feature_warn};
77
use rustc_span::source_map::Spanned;
88
use rustc_span::symbol::sym;
99
use rustc_span::{Span, Symbol};
@@ -81,7 +81,7 @@ impl<'a> PostExpansionVisitor<'a> {
8181
match abi::is_enabled(self.features, span, symbol_unescaped.as_str()) {
8282
Ok(()) => (),
8383
Err(abi::AbiDisabled::Unstable { feature, explain }) => {
84-
feature_err_issue(&self.sess, feature, span, GateIssue::Language, explain).emit();
84+
feature_err(&self.sess, feature, span, explain).emit();
8585
}
8686
Err(abi::AbiDisabled::Unrecognized) => {
8787
if self.sess.opts.pretty.map_or(true, |ppm| ppm.needs_hir()) {

compiler/rustc_feature/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
# tidy-alphabetical-start
8+
either = "1.5.0"
89
rustc_data_structures = { path = "../rustc_data_structures" }
910
rustc_span = { path = "../rustc_span" }
1011
# tidy-alphabetical-end

compiler/rustc_feature/src/lib.rs

+13-7
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ impl UnstableFeatures {
9292
}
9393
}
9494

95-
fn find_lang_feature_issue(feature: Symbol) -> Option<NonZero<u32>> {
95+
pub fn find_lang_feature_issue(feature: Symbol) -> Option<NonZero<u32>> {
9696
// Search in all the feature lists.
9797
if let Some(f) = UNSTABLE_LANG_FEATURES.iter().find(|f| f.name == feature) {
9898
return f.issue;
@@ -115,15 +115,21 @@ const fn to_nonzero(n: Option<u32>) -> Option<NonZero<u32>> {
115115
}
116116
}
117117

118-
pub enum GateIssue {
118+
pub enum GateIssues {
119119
Language,
120-
Library(Option<NonZero<u32>>),
120+
Library(Vec<NonZero<u32>>),
121121
}
122122

123-
pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZero<u32>> {
124-
match issue {
125-
GateIssue::Language => find_lang_feature_issue(feature),
126-
GateIssue::Library(lib) => lib,
123+
pub fn find_feature_issues(
124+
features: &[Symbol],
125+
issues: GateIssues,
126+
) -> impl Iterator<Item = NonZero<u32>> + use<'_> {
127+
use either::{Left, Right};
128+
match issues {
129+
GateIssues::Language => {
130+
Left(features.iter().flat_map(|&feature| find_lang_feature_issue(feature)))
131+
}
132+
GateIssues::Library(lib) => Right(lib.into_iter()),
127133
}
128134
}
129135

compiler/rustc_hir_analysis/src/check/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,11 @@ fn default_body_is_unstable(
320320
let inject_span = item_did
321321
.as_local()
322322
.and_then(|id| tcx.crate_level_attribute_injection_span(tcx.local_def_id_to_hir_id(id)));
323-
rustc_session::parse::add_feature_diagnostics_for_issue(
323+
rustc_session::parse::add_feature_diagnostics_for_issues(
324324
&mut err,
325325
&tcx.sess,
326-
feature,
327-
rustc_feature::GateIssue::Library(issue),
326+
&[feature],
327+
rustc_feature::GateIssues::Library(Vec::from_iter(issue)),
328328
false,
329329
inject_span,
330330
);

compiler/rustc_lint/src/builtin.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use rustc_ast::visit::{FnCtxt, FnKind};
2222
use rustc_ast::{self as ast, *};
2323
use rustc_ast_pretty::pprust::{self, expr_to_string};
2424
use rustc_errors::{Applicability, LintDiagnostic};
25-
use rustc_feature::{AttributeGate, BuiltinAttribute, GateIssue, Stability, deprecated_attributes};
25+
use rustc_feature::{AttributeGate, BuiltinAttribute, Stability, deprecated_attributes};
2626
use rustc_hir as hir;
2727
use rustc_hir::def::{DefKind, Res};
2828
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
@@ -2313,7 +2313,7 @@ impl EarlyLintPass for IncompleteInternalFeatures {
23132313
.filter(|(name, _)| features.incomplete(*name) || features.internal(*name))
23142314
.for_each(|(name, span)| {
23152315
if features.incomplete(name) {
2316-
let note = rustc_feature::find_feature_issue(name, GateIssue::Language)
2316+
let note = rustc_feature::find_lang_feature_issue(name)
23172317
.map(|n| BuiltinFeatureIssueNote { n });
23182318
let help =
23192319
HAS_MIN_FEATURES.contains(&name).then_some(BuiltinIncompleteFeaturesHelp);

compiler/rustc_lint/src/levels.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use rustc_ast_pretty::pprust;
22
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
33
use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
4-
use rustc_feature::{Features, GateIssue};
4+
use rustc_feature::{Features, GateIssues};
55
use rustc_hir::intravisit::{self, Visitor};
66
use rustc_hir::{CRATE_HIR_ID, HirId};
77
use rustc_index::IndexVec;
@@ -985,11 +985,11 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
985985
lint.primary_message(fluent::lint_unknown_gated_lint);
986986
lint.arg("name", lint_id.lint.name_lower());
987987
lint.note(fluent::lint_note);
988-
rustc_session::parse::add_feature_diagnostics_for_issue(
988+
rustc_session::parse::add_feature_diagnostics_for_issues(
989989
lint,
990990
&self.sess,
991-
feature,
992-
GateIssue::Language,
991+
&[feature],
992+
GateIssues::Language,
993993
lint_from_cli,
994994
None,
995995
);

compiler/rustc_middle/src/middle/stability.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_attr::{
99
};
1010
use rustc_data_structures::unord::UnordMap;
1111
use rustc_errors::{Applicability, Diag, EmissionGuarantee};
12-
use rustc_feature::GateIssue;
12+
use rustc_feature::GateIssues;
1313
use rustc_hir::def::DefKind;
1414
use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap};
1515
use rustc_hir::{self as hir, HirId};
@@ -18,7 +18,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
1818
use rustc_session::Session;
1919
use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
2020
use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer};
21-
use rustc_session::parse::feature_err_issue;
21+
use rustc_session::parse::feature_err_issues;
2222
use rustc_span::Span;
2323
use rustc_span::symbol::{Symbol, sym};
2424
use tracing::debug;
@@ -119,7 +119,8 @@ pub fn report_unstable(
119119
if is_soft {
120120
soft_handler(SOFT_UNSTABLE, span, msg)
121121
} else {
122-
let mut err = feature_err_issue(sess, feature, span, GateIssue::Library(issue), msg);
122+
let issues = Vec::from_iter(issue);
123+
let mut err = feature_err_issues(sess, &[feature], span, GateIssues::Library(issues), msg);
123124
if let Some((inner_types, msg, sugg, applicability)) = suggestion {
124125
err.span_suggestion(inner_types, msg, sugg, applicability);
125126
}

compiler/rustc_session/src/errors.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl SuggestUpgradeCompiler {
5353
#[derive(Subdiagnostic)]
5454
#[help(session_feature_diagnostic_help)]
5555
pub(crate) struct FeatureDiagnosticHelp {
56-
pub(crate) feature: Symbol,
56+
pub(crate) feature: String,
5757
}
5858

5959
#[derive(Subdiagnostic)]
@@ -63,15 +63,15 @@ pub(crate) struct FeatureDiagnosticHelp {
6363
code = "#![feature({feature})]\n"
6464
)]
6565
pub struct FeatureDiagnosticSuggestion {
66-
pub feature: Symbol,
66+
pub feature: String,
6767
#[primary_span]
6868
pub span: Span,
6969
}
7070

7171
#[derive(Subdiagnostic)]
7272
#[help(session_cli_feature_diagnostic_help)]
7373
pub(crate) struct CliFeatureDiagnosticHelp {
74-
pub(crate) feature: Symbol,
74+
pub(crate) feature: String,
7575
}
7676

7777
#[derive(Diagnostic)]

compiler/rustc_session/src/parse.rs

+19-18
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_errors::{
1212
ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan,
1313
StashKey, fallback_fluent_bundle,
1414
};
15-
use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue};
15+
use rustc_feature::{GateIssues, UnstableFeatures, find_feature_issues};
1616
use rustc_span::edition::Edition;
1717
use rustc_span::hygiene::ExpnId;
1818
use rustc_span::source_map::{FilePathMapping, SourceMap};
@@ -87,21 +87,21 @@ pub fn feature_err(
8787
span: impl Into<MultiSpan>,
8888
explain: impl Into<DiagMessage>,
8989
) -> Diag<'_> {
90-
feature_err_issue(sess, feature, span, GateIssue::Language, explain)
90+
feature_err_issues(sess, &[feature], span, GateIssues::Language, explain)
9191
}
9292

9393
/// Construct a diagnostic for a feature gate error.
9494
///
9595
/// This variant allows you to control whether it is a library or language feature.
9696
/// Almost always, you want to use this for a language feature. If so, prefer `feature_err`.
9797
#[track_caller]
98-
pub fn feature_err_issue(
99-
sess: &Session,
100-
feature: Symbol,
98+
pub fn feature_err_issues<'a>(
99+
sess: &'a Session,
100+
features: &[Symbol],
101101
span: impl Into<MultiSpan>,
102-
issue: GateIssue,
102+
issues: GateIssues,
103103
explain: impl Into<DiagMessage>,
104-
) -> Diag<'_> {
104+
) -> Diag<'a> {
105105
let span = span.into();
106106

107107
// Cancel an earlier warning for this same error, if it exists.
@@ -112,7 +112,7 @@ pub fn feature_err_issue(
112112
}
113113

114114
let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() });
115-
add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None);
115+
add_feature_diagnostics_for_issues(&mut err, sess, features, issues, false, None);
116116
err
117117
}
118118

@@ -121,7 +121,7 @@ pub fn feature_err_issue(
121121
/// This diagnostic is only a warning and *does not cause compilation to fail*.
122122
#[track_caller]
123123
pub fn feature_warn(sess: &Session, feature: Symbol, span: Span, explain: &'static str) {
124-
feature_warn_issue(sess, feature, span, GateIssue::Language, explain);
124+
feature_warn_issues(sess, &[feature], span, GateIssues::Language, explain);
125125
}
126126

127127
/// Construct a future incompatibility diagnostic for a feature gate.
@@ -133,15 +133,15 @@ pub fn feature_warn(sess: &Session, feature: Symbol, span: Span, explain: &'stat
133133
#[allow(rustc::diagnostic_outside_of_impl)]
134134
#[allow(rustc::untranslatable_diagnostic)]
135135
#[track_caller]
136-
pub fn feature_warn_issue(
136+
pub fn feature_warn_issues(
137137
sess: &Session,
138-
feature: Symbol,
138+
features: &[Symbol],
139139
span: Span,
140-
issue: GateIssue,
140+
issues: GateIssues,
141141
explain: &'static str,
142142
) {
143143
let mut err = sess.dcx().struct_span_warn(span, explain);
144-
add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None);
144+
add_feature_diagnostics_for_issues(&mut err, sess, features, issues, false, None);
145145

146146
// Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level
147147
let lint = UNSTABLE_SYNTAX_PRE_EXPANSION;
@@ -161,7 +161,7 @@ pub fn add_feature_diagnostics<G: EmissionGuarantee>(
161161
sess: &Session,
162162
feature: Symbol,
163163
) {
164-
add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false, None);
164+
add_feature_diagnostics_for_issues(err, sess, &[feature], GateIssues::Language, false, None);
165165
}
166166

167167
/// Adds the diagnostics for a feature to an existing error.
@@ -170,20 +170,21 @@ pub fn add_feature_diagnostics<G: EmissionGuarantee>(
170170
/// Almost always, you want to use this for a language feature. If so, prefer
171171
/// `add_feature_diagnostics`.
172172
#[allow(rustc::diagnostic_outside_of_impl)] // FIXME
173-
pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>(
173+
pub fn add_feature_diagnostics_for_issues<G: EmissionGuarantee>(
174174
err: &mut Diag<'_, G>,
175175
sess: &Session,
176-
feature: Symbol,
177-
issue: GateIssue,
176+
features: &[Symbol],
177+
issues: GateIssues,
178178
feature_from_cli: bool,
179179
inject_span: Option<Span>,
180180
) {
181-
if let Some(n) = find_feature_issue(feature, issue) {
181+
for n in find_feature_issues(features, issues) {
182182
err.subdiagnostic(FeatureDiagnosticForIssue { n });
183183
}
184184

185185
// #23973: do not suggest `#![feature(...)]` if we are in beta/stable
186186
if sess.psess.unstable_features.is_nightly_build() {
187+
let feature: String = features.iter().map(|s| s.as_str()).intersperse(", ").collect();
187188
if feature_from_cli {
188189
err.subdiagnostic(CliFeatureDiagnosticHelp { feature });
189190
} else if let Some(span) = inject_span {

0 commit comments

Comments
 (0)