diff --git a/Cargo.lock b/Cargo.lock index 633eeb3d3bd8e..7afb6d5f361a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4294,6 +4294,7 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_hir", + "rustc_macros", "rustc_middle", "rustc_session", "rustc_span", diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index a1233d62cb02e..708fe8719a1eb 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -1,4 +1,6 @@ -use rustc_errors::{struct_span_err, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{ + struct_span_err, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, ErrorGuaranteed, MultiSpan, +}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; @@ -476,10 +478,11 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { struct_span_err!(self, span, E0716, "temporary value dropped while borrowed",) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] fn struct_span_err_with_code>( &self, sp: S, - msg: &str, + msg: impl Into, code: DiagnosticId, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { self.infcx.tcx.sess.struct_span_err_with_code(sp, msg, code) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 8ef2974c37232..a2df072aa3119 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -6,6 +6,7 @@ #![feature(let_else)] #![feature(min_specialization)] #![feature(never_type)] +#![feature(rustc_attrs)] #![feature(stmt_expr_attributes)] #![feature(trusted_step)] #![feature(try_blocks)] diff --git a/compiler/rustc_error_messages/locales/en-US/privacy.ftl b/compiler/rustc_error_messages/locales/en-US/privacy.ftl new file mode 100644 index 0000000000000..2b0778f48caee --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/privacy.ftl @@ -0,0 +1,12 @@ +privacy-field-is-private = field `{$field_name}` of {$variant_descr} `{$def_path_str}` is private +privacy-field-is-private-is-update-syntax-label = field `{$field_name}` is private +privacy-field-is-private-label = private field + +privacy-item-is-private = {$kind} `{$descr}` is private + .label = private {$kind} +privacy-unnamed-item-is-private = {$kind} is private + .label = private {$kind} + +privacy-in-public-interface = {$vis_descr} {$kind} `{$descr}` in public interface + .label = can't leak {$vis_descr} {$kind} + .visibility-label = `{$descr}` declared as {$vis_descr} diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 7211c05432698..bc63ccd280872 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -32,6 +32,7 @@ pub use unic_langid::{langid, LanguageIdentifier}; // Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module. fluent_messages! { parser => "../locales/en-US/parser.ftl", + privacy => "../locales/en-US/privacy.ftl", typeck => "../locales/en-US/typeck.ftl", builtin_macros => "../locales/en-US/builtin_macros.ftl", } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 245719bff1202..1e57d66dd9f56 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1077,6 +1077,7 @@ impl<'a> ExtCtxt<'a> { self.current_expansion.id.expansion_cause() } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_err>( &self, sp: S, @@ -1101,9 +1102,11 @@ impl<'a> ExtCtxt<'a> { /// /// Compilation will be stopped in the near future (at the end of /// the macro expansion phase). + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_err>(&self, sp: S, msg: &str) { self.sess.parse_sess.span_diagnostic.span_err(sp, msg); } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_warn>(&self, sp: S, msg: &str) { self.sess.parse_sess.span_diagnostic.span_warn(sp, msg); } diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 86ff110eec183..c18147592dc70 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -9,6 +9,7 @@ #![feature(proc_macro_diagnostic)] #![feature(proc_macro_internals)] #![feature(proc_macro_span)] +#![feature(rustc_attrs)] #![feature(try_blocks)] #![recursion_limit = "256"] diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index fadb1c8793398..56c8635a189e1 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -406,9 +406,12 @@ impl LateLintPass<'_> for Diagnostics { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { let Some((span, def_id, substs)) = typeck_results_of_method_fn(cx, expr) else { return }; debug!(?span, ?def_id, ?substs); - if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) && - !cx.tcx.has_attr(instance.def_id(), sym::rustc_lint_diagnostics) - { + let has_attr = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) + .ok() + .and_then(|inst| inst) + .map(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics)) + .unwrap_or(false); + if !has_attr { return; } diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index f3bdd63ee6dd4..113af328a91fa 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -6,6 +6,7 @@ #![feature(let_chains)] #![feature(let_else)] #![feature(never_type)] +#![feature(rustc_attrs)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index ba325d704228d..444487648b56e 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -357,6 +357,7 @@ impl<'a> DerefMut for SnapshotParser<'a> { } impl<'a> Parser<'a> { + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub(super) fn span_err>( &self, sp: S, @@ -365,6 +366,7 @@ impl<'a> Parser<'a> { err.span_err(sp, self.diagnostic()) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_err>( &self, sp: S, diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml index d952e288a6477..5785921fb1eda 100644 --- a/compiler/rustc_privacy/Cargo.toml +++ b/compiler/rustc_privacy/Cargo.toml @@ -4,14 +4,15 @@ version = "0.0.0" edition = "2021" [dependencies] -rustc_middle = { path = "../rustc_middle" } rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } +rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } -rustc_typeck = { path = "../rustc_typeck" } +rustc_macros = { path = "../rustc_macros" } +rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } -rustc_data_structures = { path = "../rustc_data_structures" } rustc_trait_selection = { path = "../rustc_trait_selection" } +rustc_typeck = { path = "../rustc_typeck" } tracing = "0.1" diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs new file mode 100644 index 0000000000000..f33efc7f6ab7b --- /dev/null +++ b/compiler/rustc_privacy/src/errors.rs @@ -0,0 +1,75 @@ +use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; +use rustc_span::{Span, Symbol}; + +#[derive(SessionDiagnostic)] +#[error(code = "E0451", slug = "privacy-field-is-private")] +pub struct FieldIsPrivate { + #[primary_span] + pub span: Span, + pub field_name: Symbol, + pub variant_descr: &'static str, + pub def_path_str: String, + #[subdiagnostic] + pub label: FieldIsPrivateLabel, +} + +#[derive(SessionSubdiagnostic)] +pub enum FieldIsPrivateLabel { + #[label(slug = "privacy-field-is-private-is-update-syntax-label")] + IsUpdateSyntax { + #[primary_span] + span: Span, + field_name: Symbol, + }, + #[label(slug = "privacy-field-is-private-label")] + Other { + #[primary_span] + span: Span, + }, +} + +#[derive(SessionDiagnostic)] +#[error(slug = "privacy-item-is-private")] +pub struct ItemIsPrivate<'a> { + #[primary_span] + #[label] + pub span: Span, + pub kind: &'a str, + pub descr: String, +} + +#[derive(SessionDiagnostic)] +#[error(slug = "privacy-unnamed-item-is-private")] +pub struct UnnamedItemIsPrivate { + #[primary_span] + pub span: Span, + pub kind: &'static str, +} + +// Duplicate of `InPublicInterface` but with a different error code, shares the same slug. +#[derive(SessionDiagnostic)] +#[error(code = "E0445", slug = "privacy-in-public-interface")] +pub struct InPublicInterfaceTraits<'a> { + #[primary_span] + #[label] + pub span: Span, + pub vis_descr: &'static str, + pub kind: &'a str, + pub descr: String, + #[label = "visibility-label"] + pub vis_span: Span, +} + +// Duplicate of `InPublicInterfaceTraits` but with a different error code, shares the same slug. +#[derive(SessionDiagnostic)] +#[error(code = "E0446", slug = "privacy-in-public-interface")] +pub struct InPublicInterface<'a> { + #[primary_span] + #[label] + pub span: Span, + pub vis_descr: &'static str, + pub kind: &'a str, + pub descr: String, + #[label = "visibility-label"] + pub vis_span: Span, +} diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index b27c986d0f9da..238c917bbc33f 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1,15 +1,19 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(associated_type_defaults)] #![feature(control_flow_enum)] +#![feature(rustc_private)] #![feature(try_blocks)] -#![feature(associated_type_defaults)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] +#![cfg_attr(not(bootstrap), deny(rustc::untranslatable_diagnostic))] +#![cfg_attr(not(bootstrap), deny(rustc::diagnostic_outside_of_impl))] + +mod errors; use rustc_ast::MacroDef; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::intern::Interned; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID}; @@ -34,6 +38,11 @@ use std::marker::PhantomData; use std::ops::ControlFlow; use std::{cmp, fmt, mem}; +use errors::{ + FieldIsPrivate, FieldIsPrivateLabel, InPublicInterface, InPublicInterfaceTraits, ItemIsPrivate, + UnnamedItemIsPrivate, +}; + //////////////////////////////////////////////////////////////////////////////// /// Generic infrastructure used to implement specific visitors below. //////////////////////////////////////////////////////////////////////////////// @@ -935,23 +944,17 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { let hir_id = self.tcx.hir().local_def_id_to_hir_id(self.current_item); let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did(), hir_id).1; if !field.vis.is_accessible_from(def_id, self.tcx) { - let label = if in_update_syntax { - format!("field `{}` is private", field.name) - } else { - "private field".to_string() - }; - - struct_span_err!( - self.tcx.sess, + self.tcx.sess.emit_err(FieldIsPrivate { span, - E0451, - "field `{}` of {} `{}` is private", - field.name, - def.variant_descr(), - self.tcx.def_path_str(def.did()) - ) - .span_label(span, label) - .emit(); + field_name: field.name, + variant_descr: def.variant_descr(), + def_path_str: self.tcx.def_path_str(def.did()), + label: if in_update_syntax { + FieldIsPrivateLabel::IsUpdateSyntax { span, field_name: field.name } + } else { + FieldIsPrivateLabel::Other { span } + }, + }); } } } @@ -1075,11 +1078,11 @@ impl<'tcx> TypePrivacyVisitor<'tcx> { fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { let is_error = !self.item_is_accessible(def_id); if is_error { - self.tcx - .sess - .struct_span_err(self.span, &format!("{} `{}` is private", kind, descr)) - .span_label(self.span, &format!("private {}", kind)) - .emit(); + self.tcx.sess.emit_err(ItemIsPrivate { + span: self.span, + kind, + descr: descr.to_string(), + }); } is_error } @@ -1250,13 +1253,10 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { hir::QPath::TypeRelative(_, segment) => Some(segment.ident.to_string()), }; let kind = kind.descr(def_id); - let msg = match name { - Some(name) => format!("{} `{}` is private", kind, name), - None => format!("{} is private", kind), + let _ = match name { + Some(name) => sess.emit_err(ItemIsPrivate { span, kind, descr: name }), + None => sess.emit_err(UnnamedItemIsPrivate { span, kind }), }; - sess.struct_span_err(span, &msg) - .span_label(span, &format!("private {}", kind)) - .emit(); return; } } @@ -1753,22 +1753,31 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { } } }; - let make_msg = || format!("{} {} `{}` in public interface", vis_descr, kind, descr); let span = self.tcx.def_span(self.item_def_id.to_def_id()); if self.has_old_errors || self.in_assoc_ty || self.tcx.resolutions(()).has_pub_restricted { - let mut err = if kind == "trait" { - struct_span_err!(self.tcx.sess, span, E0445, "{}", make_msg()) - } else { - struct_span_err!(self.tcx.sess, span, E0446, "{}", make_msg()) - }; + let descr = descr.to_string(); let vis_span = self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id)); - err.span_label(span, format!("can't leak {} {}", vis_descr, kind)); - err.span_label(vis_span, format!("`{}` declared as {}", descr, vis_descr)); - err.emit(); + if kind == "trait" { + self.tcx.sess.emit_err(InPublicInterfaceTraits { + span, + vis_descr, + kind, + descr, + vis_span, + }); + } else { + self.tcx.sess.emit_err(InPublicInterface { + span, + vis_descr, + kind, + descr, + vis_span, + }); + } } else { let err_code = if kind == "trait" { "E0445" } else { "E0446" }; self.tcx.struct_span_lint_hir( @@ -1776,7 +1785,12 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { hir_id, span, |lint| { - lint.build(&format!("{} (error {})", make_msg(), err_code)).emit(); + lint.build(&format!( + "{} (error {})", + format!("{} {} `{}` in public interface", vis_descr, kind, descr), + err_code + )) + .emit(); }, ); } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index f1814eebfa6fd..b5058fd699aca 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -280,6 +280,7 @@ impl Session { self.crate_types.set(crate_types).expect("`crate_types` was initialized twice") } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_warn>( &self, sp: S, @@ -287,6 +288,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_warn(sp, msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_warn_with_expectation>( &self, sp: S, @@ -295,6 +297,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_warn_with_expectation(sp, msg, id) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_warn_with_code>( &self, sp: S, @@ -303,9 +306,11 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_warn_with_code(sp, msg, code) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_warn(&self, msg: impl Into) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_warn(msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_warn_with_expectation( &self, msg: impl Into, @@ -313,6 +318,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_warn_with_expectation(msg, id) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_allow>( &self, sp: S, @@ -320,9 +326,11 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_allow(sp, msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_allow(&self, msg: impl Into) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_allow(msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_expect( &self, msg: impl Into, @@ -330,6 +338,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_expect(msg, id) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_err>( &self, sp: S, @@ -337,6 +346,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_span_err(sp, msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_err_with_code>( &self, sp: S, @@ -346,12 +356,14 @@ impl Session { self.diagnostic().struct_span_err_with_code(sp, msg, code) } // FIXME: This method should be removed (every error should have an associated error code). + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_err( &self, msg: impl Into, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.parse_sess.struct_err(msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_err_with_code( &self, msg: impl Into, @@ -359,6 +371,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_err_with_code(msg, code) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_warn_with_code( &self, msg: impl Into, @@ -366,6 +379,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_warn_with_code(msg, code) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_fatal>( &self, sp: S, @@ -373,6 +387,7 @@ impl Session { ) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_span_fatal(sp, msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_fatal_with_code>( &self, sp: S, @@ -381,13 +396,16 @@ impl Session { ) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_span_fatal_with_code(sp, msg, code) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_fatal(&self, msg: impl Into) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_fatal(msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_fatal>(&self, sp: S, msg: impl Into) -> ! { self.diagnostic().span_fatal(sp, msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_fatal_with_code>( &self, sp: S, @@ -396,9 +414,11 @@ impl Session { ) -> ! { self.diagnostic().span_fatal_with_code(sp, msg, code) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn fatal(&self, msg: impl Into) -> ! { self.diagnostic().fatal(msg).raise() } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_err_or_warn>( &self, is_warning: bool, @@ -411,6 +431,7 @@ impl Session { self.span_err(sp, msg); } } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_err>( &self, sp: S, @@ -418,6 +439,7 @@ impl Session { ) -> ErrorGuaranteed { self.diagnostic().span_err(sp, msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_err_with_code>( &self, sp: S, @@ -426,6 +448,7 @@ impl Session { ) { self.diagnostic().span_err_with_code(sp, msg, code) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn err(&self, msg: impl Into) -> ErrorGuaranteed { self.diagnostic().err(msg) }