Skip to content

Commit 95e0a2c

Browse files
committed
Auto merge of #68725 - jumbatm:invert-control-in-struct_lint_level, r=Centril
Invert control in struct_lint_level. Closes #67927 Changes the `struct_lint*` methods to take a `decorate` function instead of a message string. This decorate function is also responsible for eventually stashing, emitting or cancelling the diagnostic. If the lint was allowed after all, the decorate function is not run at all, saving us from spending time formatting messages (and potentially other expensive work) for lints that don't end up being emitted. r? @Centril
2 parents b6690a8 + b959da2 commit 95e0a2c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1795
-1611
lines changed

src/librustc/infer/error_reporting/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1701,7 +1701,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
17011701
sub: Region<'tcx>,
17021702
) {
17031703
self.construct_generic_bound_failure(region_scope_tree, span, origin, bound_kind, sub)
1704-
.emit()
1704+
.emit();
17051705
}
17061706

17071707
pub fn construct_generic_bound_failure(

src/librustc/lint.rs

+138-106
Original file line numberDiff line numberDiff line change
@@ -174,132 +174,164 @@ impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
174174
}
175175
}
176176

177-
pub fn struct_lint_level<'a>(
178-
sess: &'a Session,
177+
pub struct LintDiagnosticBuilder<'a>(DiagnosticBuilder<'a>);
178+
179+
impl<'a> LintDiagnosticBuilder<'a> {
180+
/// Return the inner DiagnosticBuilder, first setting the primary message to `msg`.
181+
pub fn build(mut self, msg: &str) -> DiagnosticBuilder<'a> {
182+
self.0.set_primary_message(msg);
183+
self.0
184+
}
185+
186+
/// Create a LintDiagnosticBuilder from some existing DiagnosticBuilder.
187+
pub fn new(err: DiagnosticBuilder<'a>) -> LintDiagnosticBuilder<'a> {
188+
LintDiagnosticBuilder(err)
189+
}
190+
}
191+
192+
pub fn struct_lint_level<'s, 'd>(
193+
sess: &'s Session,
179194
lint: &'static Lint,
180195
level: Level,
181196
src: LintSource,
182197
span: Option<MultiSpan>,
183-
msg: &str,
184-
) -> DiagnosticBuilder<'a> {
185-
let mut err = match (level, span) {
186-
(Level::Allow, _) => return sess.diagnostic().struct_dummy(),
187-
(Level::Warn, Some(span)) => sess.struct_span_warn(span, msg),
188-
(Level::Warn, None) => sess.struct_warn(msg),
189-
(Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => sess.struct_span_err(span, msg),
190-
(Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(msg),
191-
};
198+
decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>) + 'd,
199+
) {
200+
// Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to
201+
// the "real" work.
202+
fn struct_lint_level_impl(
203+
sess: &'s Session,
204+
lint: &'static Lint,
205+
level: Level,
206+
src: LintSource,
207+
span: Option<MultiSpan>,
208+
decorate: Box<dyn for<'b> FnOnce(LintDiagnosticBuilder<'b>) + 'd>,
209+
) {
210+
let mut err = match (level, span) {
211+
(Level::Allow, _) => {
212+
return;
213+
}
214+
(Level::Warn, Some(span)) => sess.struct_span_warn(span, ""),
215+
(Level::Warn, None) => sess.struct_warn(""),
216+
(Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => {
217+
sess.struct_span_err(span, "")
218+
}
219+
(Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(""),
220+
};
192221

193-
// Check for future incompatibility lints and issue a stronger warning.
194-
let lint_id = LintId::of(lint);
195-
let future_incompatible = lint.future_incompatible;
196-
197-
// If this code originates in a foreign macro, aka something that this crate
198-
// did not itself author, then it's likely that there's nothing this crate
199-
// can do about it. We probably want to skip the lint entirely.
200-
if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) {
201-
// Any suggestions made here are likely to be incorrect, so anything we
202-
// emit shouldn't be automatically fixed by rustfix.
203-
err.allow_suggestions(false);
204-
205-
// If this is a future incompatible lint it'll become a hard error, so
206-
// we have to emit *something*. Also allow lints to whitelist themselves
207-
// on a case-by-case basis for emission in a foreign macro.
208-
if future_incompatible.is_none() && !lint.report_in_external_macro {
209-
err.cancel();
210-
// Don't continue further, since we don't want to have
211-
// `diag_span_note_once` called for a diagnostic that isn't emitted.
212-
return err;
222+
// Check for future incompatibility lints and issue a stronger warning.
223+
let lint_id = LintId::of(lint);
224+
let future_incompatible = lint.future_incompatible;
225+
226+
// If this code originates in a foreign macro, aka something that this crate
227+
// did not itself author, then it's likely that there's nothing this crate
228+
// can do about it. We probably want to skip the lint entirely.
229+
if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) {
230+
// Any suggestions made here are likely to be incorrect, so anything we
231+
// emit shouldn't be automatically fixed by rustfix.
232+
err.allow_suggestions(false);
233+
234+
// If this is a future incompatible lint it'll become a hard error, so
235+
// we have to emit *something*. Also allow lints to whitelist themselves
236+
// on a case-by-case basis for emission in a foreign macro.
237+
if future_incompatible.is_none() && !lint.report_in_external_macro {
238+
err.cancel();
239+
// Don't continue further, since we don't want to have
240+
// `diag_span_note_once` called for a diagnostic that isn't emitted.
241+
return;
242+
}
213243
}
214-
}
215244

216-
let name = lint.name_lower();
217-
match src {
218-
LintSource::Default => {
219-
sess.diag_note_once(
220-
&mut err,
221-
DiagnosticMessageId::from(lint),
222-
&format!("`#[{}({})]` on by default", level.as_str(), name),
223-
);
224-
}
225-
LintSource::CommandLine(lint_flag_val) => {
226-
let flag = match level {
227-
Level::Warn => "-W",
228-
Level::Deny => "-D",
229-
Level::Forbid => "-F",
230-
Level::Allow => panic!(),
231-
};
232-
let hyphen_case_lint_name = name.replace("_", "-");
233-
if lint_flag_val.as_str() == name {
245+
let name = lint.name_lower();
246+
match src {
247+
LintSource::Default => {
234248
sess.diag_note_once(
235249
&mut err,
236250
DiagnosticMessageId::from(lint),
237-
&format!(
238-
"requested on the command line with `{} {}`",
239-
flag, hyphen_case_lint_name
240-
),
241-
);
242-
} else {
243-
let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-");
244-
sess.diag_note_once(
245-
&mut err,
246-
DiagnosticMessageId::from(lint),
247-
&format!(
248-
"`{} {}` implied by `{} {}`",
249-
flag, hyphen_case_lint_name, flag, hyphen_case_flag_val
250-
),
251+
&format!("`#[{}({})]` on by default", level.as_str(), name),
251252
);
252253
}
253-
}
254-
LintSource::Node(lint_attr_name, src, reason) => {
255-
if let Some(rationale) = reason {
256-
err.note(&rationale.as_str());
254+
LintSource::CommandLine(lint_flag_val) => {
255+
let flag = match level {
256+
Level::Warn => "-W",
257+
Level::Deny => "-D",
258+
Level::Forbid => "-F",
259+
Level::Allow => panic!(),
260+
};
261+
let hyphen_case_lint_name = name.replace("_", "-");
262+
if lint_flag_val.as_str() == name {
263+
sess.diag_note_once(
264+
&mut err,
265+
DiagnosticMessageId::from(lint),
266+
&format!(
267+
"requested on the command line with `{} {}`",
268+
flag, hyphen_case_lint_name
269+
),
270+
);
271+
} else {
272+
let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-");
273+
sess.diag_note_once(
274+
&mut err,
275+
DiagnosticMessageId::from(lint),
276+
&format!(
277+
"`{} {}` implied by `{} {}`",
278+
flag, hyphen_case_lint_name, flag, hyphen_case_flag_val
279+
),
280+
);
281+
}
257282
}
258-
sess.diag_span_note_once(
259-
&mut err,
260-
DiagnosticMessageId::from(lint),
261-
src,
262-
"the lint level is defined here",
263-
);
264-
if lint_attr_name.as_str() != name {
265-
let level_str = level.as_str();
266-
sess.diag_note_once(
283+
LintSource::Node(lint_attr_name, src, reason) => {
284+
if let Some(rationale) = reason {
285+
err.note(&rationale.as_str());
286+
}
287+
sess.diag_span_note_once(
267288
&mut err,
268289
DiagnosticMessageId::from(lint),
269-
&format!(
270-
"`#[{}({})]` implied by `#[{}({})]`",
271-
level_str, name, level_str, lint_attr_name
272-
),
290+
src,
291+
"the lint level is defined here",
273292
);
293+
if lint_attr_name.as_str() != name {
294+
let level_str = level.as_str();
295+
sess.diag_note_once(
296+
&mut err,
297+
DiagnosticMessageId::from(lint),
298+
&format!(
299+
"`#[{}({})]` implied by `#[{}({})]`",
300+
level_str, name, level_str, lint_attr_name
301+
),
302+
);
303+
}
274304
}
275305
}
276-
}
277306

278-
err.code(DiagnosticId::Lint(name));
279-
280-
if let Some(future_incompatible) = future_incompatible {
281-
const STANDARD_MESSAGE: &str = "this was previously accepted by the compiler but is being phased out; \
282-
it will become a hard error";
283-
284-
let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) {
285-
"once this method is added to the standard library, \
286-
the ambiguity may cause an error or change in behavior!"
287-
.to_owned()
288-
} else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) {
289-
"this borrowing pattern was not meant to be accepted, \
290-
and may become a hard error in the future"
291-
.to_owned()
292-
} else if let Some(edition) = future_incompatible.edition {
293-
format!("{} in the {} edition!", STANDARD_MESSAGE, edition)
294-
} else {
295-
format!("{} in a future release!", STANDARD_MESSAGE)
296-
};
297-
let citation = format!("for more information, see {}", future_incompatible.reference);
298-
err.warn(&explanation);
299-
err.note(&citation);
300-
}
307+
err.code(DiagnosticId::Lint(name));
308+
309+
if let Some(future_incompatible) = future_incompatible {
310+
const STANDARD_MESSAGE: &str = "this was previously accepted by the compiler but is being phased out; \
311+
it will become a hard error";
312+
313+
let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) {
314+
"once this method is added to the standard library, \
315+
the ambiguity may cause an error or change in behavior!"
316+
.to_owned()
317+
} else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) {
318+
"this borrowing pattern was not meant to be accepted, \
319+
and may become a hard error in the future"
320+
.to_owned()
321+
} else if let Some(edition) = future_incompatible.edition {
322+
format!("{} in the {} edition!", STANDARD_MESSAGE, edition)
323+
} else {
324+
format!("{} in a future release!", STANDARD_MESSAGE)
325+
};
326+
let citation = format!("for more information, see {}", future_incompatible.reference);
327+
err.warn(&explanation);
328+
err.note(&citation);
329+
}
301330

302-
return err;
331+
// Finally, run `decorate`. This function is also responsible for emitting the diagnostic.
332+
decorate(LintDiagnosticBuilder::new(err));
333+
}
334+
struct_lint_level_impl(sess, lint, level, src, span, Box::new(decorate))
303335
}
304336

305337
/// Returns whether `span` originates in a foreign crate's external macro.

src/librustc/middle/stability.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,13 @@ fn late_report_deprecation(
222222
return;
223223
}
224224

225-
let mut diag = tcx.struct_span_lint_hir(lint, hir_id, span, message);
226-
if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
227-
deprecation_suggestion(&mut diag, suggestion, span);
228-
}
229-
diag.emit();
225+
tcx.struct_span_lint_hir(lint, hir_id, span, |lint| {
226+
let mut diag = lint.build(message);
227+
if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
228+
deprecation_suggestion(&mut diag, suggestion, span);
229+
}
230+
diag.emit()
231+
});
230232
if hir_id == hir::DUMMY_HIR_ID {
231233
span_bug!(span, "emitted a {} lint with dummy HIR id: {:?}", lint.name, def_id);
232234
}
@@ -387,8 +389,11 @@ impl<'tcx> TyCtxt<'tcx> {
387389
/// Additionally, this function will also check if the item is deprecated. If so, and `id` is
388390
/// not `None`, a deprecated lint attached to `id` will be emitted.
389391
pub fn check_stability(self, def_id: DefId, id: Option<HirId>, span: Span) {
390-
let soft_handler =
391-
|lint, span, msg: &_| self.lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg);
392+
let soft_handler = |lint, span, msg: &_| {
393+
self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| {
394+
lint.build(msg).emit()
395+
})
396+
};
392397
match self.eval_stability(def_id, id, span) {
393398
EvalResult::Allow => {}
394399
EvalResult::Deny { feature, reason, issue, is_soft } => {

0 commit comments

Comments
 (0)