diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index cf643931717be..bf91d6d93883b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1019,10 +1019,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let Some(hir_id) = terminator.source_info.scope.lint_root(&self.mir.source_scopes) { - let msg = "tail calling a function marked with `#[track_caller]` has no special effect"; - bx.tcx().node_lint(TAIL_CALL_TRACK_CALLER, hir_id, |d| { - _ = d.primary_message(msg).span(fn_span) - }); + bx.tcx().emit_node_lint(TAIL_CALL_TRACK_CALLER, hir_id, rustc_errors::DiagDecorator(|d| { + _ = d.primary_message("tail calling a function marked with `#[track_caller]` has no special effect").span(fn_span) + })); } let instance = ty::Instance::resolve_for_fn_ptr( diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 608bc7dffd9c0..b90d5b40bec6d 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -736,12 +736,18 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let trait_name = self.tcx.item_name(pick.item.container_id(self.tcx)); let import_span = self.tcx.hir_span_if_local(pick.import_ids[0].to_def_id()).unwrap(); - self.tcx.node_lint(AMBIGUOUS_GLOB_IMPORTED_TRAITS, segment.hir_id, |diag| { - diag.primary_message(format!("Use of ambiguously glob imported trait `{trait_name}`")) + self.tcx.emit_node_lint( + AMBIGUOUS_GLOB_IMPORTED_TRAITS, + segment.hir_id, + rustc_errors::DiagDecorator(|diag| { + diag.primary_message(format!( + "Use of ambiguously glob imported trait `{trait_name}`" + )) .span(segment.ident.span) .span_label(import_span, format!("`{trait_name}` imported ambiguously here")) .help(format!("Import `{trait_name}` explicitly")); - }); + }), + ); } fn upcast( diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d7a0a02f085a5..ecf71f93cf5ea 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -514,7 +514,7 @@ pub trait LintContext { // set the span in their `decorate` function (preferably using set_span). /// Emit a lint at the appropriate level, with an optional associated span. /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature + /// [`diag_lint_level`]: rustc_middle::lint::diag_lint_level#decorate-signature #[track_caller] fn opt_span_diag_lint>( &self, diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 1c37dd0f82f42..f302f18f3b47e 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -293,201 +293,12 @@ fn explain_lint_level_source( } } -/// The innermost function for emitting lints. -/// -/// If you are looking to implement a lint, look for higher level functions, -/// for example: -/// - [`TyCtxt::emit_node_span_lint`] -/// - [`TyCtxt::node_lint`] -/// - `LintContext::opt_span_lint` -/// -/// ## `decorate` -/// -/// It is not intended to call `emit`/`cancel` on the `Diag` passed in the `decorate` callback. -#[track_caller] -pub fn lint_level( - sess: &Session, - lint: &'static Lint, - level: LevelAndSource, - span: Option, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), -) { - // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to - // the "real" work. - #[track_caller] - fn lint_level_impl( - sess: &Session, - lint: &'static Lint, - level: LevelAndSource, - span: Option, - decorate: Box FnOnce(&'b mut Diag<'a, ()>)>, - ) { - let LevelAndSource { level, lint_id, src } = level; - - // Check for future incompatibility lints and issue a stronger warning. - let future_incompatible = lint.future_incompatible; - - let has_future_breakage = future_incompatible.map_or( - // Default allow lints trigger too often for testing. - sess.opts.unstable_opts.future_incompat_test && lint.default_level != Level::Allow, - |incompat| incompat.report_in_deps, - ); - - // Convert lint level to error level. - let err_level = match level { - Level::Allow => { - if has_future_breakage { - rustc_errors::Level::Allow - } else { - return; - } - } - Level::Expect => { - // This case is special as we actually allow the lint itself in this context, but - // we can't return early like in the case for `Level::Allow` because we still - // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`. - // - // We can also not mark the lint expectation as fulfilled here right away, as it - // can still be cancelled in the decorate function. All of this means that we simply - // create a `Diag` and continue as we would for warnings. - rustc_errors::Level::Expect - } - Level::ForceWarn => rustc_errors::Level::ForceWarning, - Level::Warn => rustc_errors::Level::Warning, - Level::Deny | Level::Forbid => rustc_errors::Level::Error, - }; - let mut err = Diag::new(sess.dcx(), err_level, ""); - if let Some(span) = span { - err.span(span); - } - if let Some(lint_id) = lint_id { - err.lint_id(lint_id); - } - - // If this code originates in a foreign macro, aka something that this crate - // did not itself author, then it's likely that there's nothing this crate - // can do about it. We probably want to skip the lint entirely. - if err.span.primary_spans().iter().any(|s| s.in_external_macro(sess.source_map())) { - // Any suggestions made here are likely to be incorrect, so anything we - // emit shouldn't be automatically fixed by rustfix. - err.disable_suggestions(); - - // If this is a future incompatible that is not an edition fixing lint - // it'll become a hard error, so we have to emit *something*. Also, - // if this lint occurs in the expansion of a macro from an external crate, - // allow individual lints to opt-out from being reported. - let incompatible = future_incompatible.is_some_and(|f| f.reason.edition().is_none()); - - // In rustc, for the find_attr macro, we want to always emit this. - // This completely circumvents normal lint checking, which usually doesn't happen for macros from other crates. - // However, we kind of want that when using find_attr from another rustc crate. So we cheat a little. - let is_in_find_attr = sess.enable_internal_lints() - && err.span.primary_spans().iter().any(|s| { - s.source_callee().is_some_and( - |i| matches!(i.kind, ExpnKind::Macro(_, name) if name.as_str() == "find_attr") - ) - }); - - if !incompatible && !lint.report_in_external_macro && !is_in_find_attr { - err.cancel(); - - // Don't continue further, since we don't want to have - // `diag_span_note_once` called for a diagnostic that isn't emitted. - return; - } - } - - err.is_lint(lint.name_lower(), has_future_breakage); - - // Lint diagnostics that are covered by the expect level will not be emitted outside - // the compiler. It is therefore not necessary to add any information for the user. - // This will therefore directly call the decorate function which will in turn emit - // the diagnostic. - if let Level::Expect = level { - decorate(&mut err); - err.emit(); - return; - } - - if let Some(future_incompatible) = future_incompatible { - let explanation = match future_incompatible.reason { - FutureIncompatibilityReason::FutureReleaseError(_) => { - "this was previously accepted by the compiler but is being phased out; \ - it will become a hard error in a future release!" - .to_owned() - } - FutureIncompatibilityReason::FutureReleaseSemanticsChange(_) => { - "this will change its meaning in a future release!".to_owned() - } - FutureIncompatibilityReason::EditionError(EditionFcw { edition, .. }) => { - let current_edition = sess.edition(); - format!( - "this is accepted in the current edition (Rust {current_edition}) but is a hard error in Rust {edition}!" - ) - } - FutureIncompatibilityReason::EditionSemanticsChange(EditionFcw { - edition, .. - }) => { - format!("this changes meaning in Rust {edition}") - } - FutureIncompatibilityReason::EditionAndFutureReleaseError(EditionFcw { - edition, - .. - }) => { - format!( - "this was previously accepted by the compiler but is being phased out; \ - it will become a hard error in Rust {edition} and in a future release in all editions!" - ) - } - FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange( - EditionFcw { edition, .. }, - ) => { - format!( - "this changes meaning in Rust {edition} and in a future release in all editions!" - ) - } - FutureIncompatibilityReason::Custom(reason, _) => reason.to_owned(), - FutureIncompatibilityReason::Unreachable => unreachable!(), - }; - - if future_incompatible.explain_reason { - err.warn(explanation); - } - - let citation = - format!("for more information, see {}", future_incompatible.reason.reference()); - err.note(citation); - } - - // Finally, run `decorate`. `decorate` can call `trimmed_path_str` (directly or indirectly), - // so we need to make sure when we do call `decorate` that the diagnostic is eventually - // emitted or we'll get a `must_produce_diag` ICE. - // - // When is a diagnostic *eventually* emitted? Well, that is determined by 2 factors: - // 1. If the corresponding `rustc_errors::Level` is beyond warning, i.e. `ForceWarning(_)` - // or `Error`, then the diagnostic will be emitted regardless of CLI options. - // 2. If the corresponding `rustc_errors::Level` is warning, then that can be affected by - // `-A warnings` or `--cap-lints=xxx` on the command line. In which case, the diagnostic - // will be emitted if `can_emit_warnings` is true. - let skip = err_level == rustc_errors::Level::Warning && !sess.dcx().can_emit_warnings(); - - if !skip { - decorate(&mut err); - } - - explain_lint_level_source(sess, lint, level, src, &mut err); - err.emit() - } - lint_level_impl(sess, lint, level, span, Box::new(decorate)) -} - /// The innermost function for emitting lints implementing the [`trait@Diagnostic`] trait. /// /// If you are looking to implement a lint, look for higher level functions, /// for example: /// /// - [`TyCtxt::emit_node_span_lint`] -/// - [`TyCtxt::node_lint`] /// - `LintContext::opt_span_lint` /// /// This function will replace `lint_level` once all its callers have been replaced diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a99e685c1308e..11f2d575a6598 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -55,7 +55,7 @@ use crate::dep_graph::dep_node::make_metadata; use crate::dep_graph::{DepGraph, DepKindVTable, DepNodeIndex}; use crate::ich::StableHashingContext; use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind}; -use crate::lint::{diag_lint_level, lint_level}; +use crate::lint::diag_lint_level; use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature}; use crate::middle::resolve_bound_vars; @@ -2585,20 +2585,6 @@ impl<'tcx> TyCtxt<'tcx> { diag_lint_level(self.sess, lint, level, None, decorator); } - /// Emit a lint at the appropriate level for a hir node. - /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature - #[track_caller] - pub fn node_lint( - self, - lint: &'static Lint, - id: HirId, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), - ) { - let level = self.lint_level_at_node(lint, id); - lint_level(self.sess, lint, level, None, decorate); - } - pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx [TraitCandidate<'tcx>]> { let map = self.in_scope_traits_map(id.owner)?; let candidates = map.get(&id.local_id)?; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index bb41758c7a2e5..0813816b470ac 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -400,16 +400,16 @@ pub(crate) fn run_global_ctxt( {}/rustdoc/how-to-write-documentation.html", crate::DOC_RUST_LANG_ORG_VERSION ); - tcx.node_lint( + tcx.emit_node_lint( crate::lint::MISSING_CRATE_LEVEL_DOCS, DocContext::as_local_hir_id(tcx, krate.module.item_id).unwrap(), - |lint| { + rustc_errors::DiagDecorator(|lint| { if let Some(local_def_id) = krate.module.item_id.as_local_def_id() { lint.span(tcx.def_span(local_def_id)); } lint.primary_message("no documentation found for this crate's top-level module"); lint.help(help); - }, + }), ); } diff --git a/tests/ui/imports/ambiguous-trait-in-scope.stderr b/tests/ui/imports/ambiguous-trait-in-scope.stderr index cac1f4bb73fb1..9360e104bf24f 100644 --- a/tests/ui/imports/ambiguous-trait-in-scope.stderr +++ b/tests/ui/imports/ambiguous-trait-in-scope.stderr @@ -7,9 +7,9 @@ LL | use m2::*; LL | 0u8.method1(); | ^^^^^^^ | + = help: Import `Trait` explicitly = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #147992 - = help: Import `Trait` explicitly = note: `#[warn(ambiguous_glob_imported_traits)]` (part of `#[warn(future_incompatible)]`) on by default error[E0599]: no method named `method2` found for type `u8` in the current scope @@ -49,9 +49,9 @@ LL | use m2::*; LL | 0u8.method2(); | ^^^^^^^ | + = help: Import `Trait` explicitly = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #147992 - = help: Import `Trait` explicitly warning: Use of ambiguously glob imported trait `Trait` --> $DIR/ambiguous-trait-in-scope.rs:49:9 @@ -62,9 +62,9 @@ LL | use m2_reexport::*; LL | 0u8.method1(); | ^^^^^^^ | + = help: Import `Trait` explicitly = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #147992 - = help: Import `Trait` explicitly error[E0599]: no method named `method2` found for type `u8` in the current scope --> $DIR/ambiguous-trait-in-scope.rs:51:9 @@ -88,9 +88,9 @@ LL | use ambig_reexport::*; LL | 0u8.method1(); | ^^^^^^^ | + = help: Import `Trait` explicitly = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #147992 - = help: Import `Trait` explicitly error[E0599]: no method named `method2` found for type `u8` in the current scope --> $DIR/ambiguous-trait-in-scope.rs:58:9 @@ -115,9 +115,9 @@ LL | use external::m2::*; LL | 0u8.method1(); | ^^^^^^^ | + = help: Import `Trait` explicitly = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #147992 - = help: Import `Trait` explicitly error[E0599]: no method named `method2` found for type `u8` in the current scope --> $DIR/ambiguous-trait-in-scope.rs:66:9 @@ -142,9 +142,9 @@ LL | use external::m2_reexport::*; LL | 0u8.method1(); | ^^^^^^^ | + = help: Import `Trait` explicitly = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #147992 - = help: Import `Trait` explicitly error[E0599]: no method named `method2` found for type `u8` in the current scope --> $DIR/ambiguous-trait-in-scope.rs:74:9 @@ -168,9 +168,9 @@ LL | use external::ambig_reexport::*; LL | 0u8.method1(); | ^^^^^^^ | + = help: Import `Trait` explicitly = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #147992 - = help: Import `Trait` explicitly error[E0599]: no method named `method2` found for type `u8` in the current scope --> $DIR/ambiguous-trait-in-scope.rs:81:9