diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 28e148bddb220..1c6d0495ecfa1 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -1,6 +1,9 @@ +use std::borrow::{Borrow, Cow}; +use std::cmp; use std::fmt::{self, Write}; +use std::iter; +use std::ops::Bound; use std::ops::Deref; -use std::{borrow::Borrow, cmp, iter, ops::Bound}; use rustc_index::Idx; use tracing::debug; @@ -32,7 +35,7 @@ where pub trait LayoutCalculator { type TargetDataLayoutRef: Borrow; - fn delayed_bug(&self, txt: String); + fn delayed_bug(&self, txt: impl Into>); fn current_data_layout(&self) -> Self::TargetDataLayoutRef; fn scalar_pair( diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index fd717e82d26df..7fd419f62e459 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -22,6 +22,7 @@ use std::collections::hash_map::Entry; use std::fmt::Write; impl<'a, 'hir> LoweringContext<'a, 'hir> { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub(crate) fn lower_inline_asm( &mut self, sp: Span, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 85b599902b959..41f7418ddde42 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1513,6 +1513,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn lower_expr_yield(&mut self, span: Span, opt_expr: Option<&Expr>) -> hir::ExprKind<'hir> { let yielded = opt_expr.as_ref().map(|x| self.lower_expr(x)).unwrap_or_else(|| self.expr_unit(span)); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 087d240b0d50f..37ee9a5140a06 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2272,6 +2272,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.expr_block(block) } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn lower_array_length(&mut self, c: &AnonConst) -> hir::ArrayLen { match c.value.kind { ExprKind::Underscore => { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index a28fcb0077948..d9e3f028697e0 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -17,6 +17,7 @@ use crate::errors; macro_rules! gate { ($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{ if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable feature_err(&$visitor.sess, sym::$feature, $span, $explain).emit(); } }}; @@ -34,6 +35,7 @@ macro_rules! gate { macro_rules! gate_alt { ($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr) => {{ if !$has_feature && !$span.allows_unstable($name) { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable feature_err(&$visitor.sess, $name, $span, $explain).emit(); } }}; @@ -73,6 +75,7 @@ struct PostExpansionVisitor<'a> { } impl<'a> PostExpansionVisitor<'a> { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn check_abi(&self, abi: ast::StrLit, constness: ast::Const) { let ast::StrLit { symbol_unescaped, span, .. } = abi; @@ -579,6 +582,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { if let Ok(snippet) = sm.span_to_snippet(span) && snippet == "!" { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable feature_err(sess, sym::never_patterns, span, "`!` patterns are experimental") .emit(); } else { diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index eb25b910c7baf..814104ec78c49 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -516,6 +516,7 @@ pub struct Condition { } /// Tests if a cfg-pattern matches the cfg set +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn cfg_matches( cfg: &ast::MetaItem, sess: &Session, @@ -566,6 +567,7 @@ fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Feat } } +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) { let (cfg, feature, has_feature) = gated_cfg; if !has_feature(features) && !cfg_span.allows_unstable(*feature) { @@ -592,6 +594,7 @@ fn parse_version(s: Symbol) -> Option { /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to /// evaluate individual items. +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn eval_condition( cfg: &ast::MetaItem, sess: &Session, diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 53e8ac121bbce..560928d394145 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -76,6 +76,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// LL | for (key, value) in dict { /// | ^^^^ /// ``` + #[allow(rustc::diagnostic_outside_of_impl)] // FIXME pub(super) fn add_moved_or_invoked_closure_note( &self, location: Location, @@ -585,6 +586,7 @@ impl UseSpans<'_> { } /// Add a span label to the arguments of the closure, if it exists. + #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn args_subdiag( self, dcx: &rustc_errors::DiagCtxt, @@ -598,6 +600,7 @@ impl UseSpans<'_> { /// Add a span label to the use of the captured variable, if it exists. /// only adds label to the `path_span` + #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn var_path_only_subdiag( self, dcx: &rustc_errors::DiagCtxt, @@ -635,6 +638,7 @@ impl UseSpans<'_> { } /// Add a subdiagnostic to the use of the captured variable, if it exists. + #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn var_subdiag( self, dcx: &rustc_errors::DiagCtxt, @@ -1008,6 +1012,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.borrow_spans(span, borrow.reserve_location) } + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn explain_captures( &mut self, err: &mut Diag<'_>, diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 3765dfe5db53c..c06bf94a6fd5f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -201,6 +201,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // For generic associated types (GATs) which implied 'static requirement // from higher-ranked trait bounds (HRTB). Try to locate span of the trait // and the span which bounded to the trait for adding 'static lifetime suggestion + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn suggest_static_lifetime_for_gat_from_hrtb( &self, diag: &mut Diag<'_>, @@ -254,9 +256,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { hrtb_bounds.iter().for_each(|bound| { let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }, _) = bound else { return; }; - // FIXME: make this translatable - #[allow(rustc::diagnostic_outside_of_impl)] - #[allow(rustc::untranslatable_diagnostic)] diag.span_note( *trait_span, "due to current limitations in the borrow checker, this implies a `'static` lifetime" @@ -580,6 +579,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// executing... /// = note: ...therefore, returned references to captured variables will escape the closure /// ``` + #[allow(rustc::diagnostic_outside_of_impl)] // FIXME fn report_fnmut_error( &self, errci: &ErrorConstraintInfo<'tcx>, @@ -761,6 +761,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// | ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it /// | is returning data with lifetime `'b` /// ``` + #[allow(rustc::diagnostic_outside_of_impl)] // FIXME fn report_general_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'tcx> { let ErrorConstraintInfo { fr, @@ -822,6 +823,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// LL | fn iter_values_anon(&self) -> impl Iterator + 'a { /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /// ``` + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn add_static_impl_trait_suggestion( &self, diag: &mut Diag<'_>, @@ -972,6 +975,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.suggest_constrain_dyn_trait_in_impl(diag, &visitor.0, ident, self_ty); } + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable #[instrument(skip(self, err), level = "debug")] fn suggest_constrain_dyn_trait_in_impl( &self, @@ -1034,6 +1039,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag); } + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn suggest_move_on_borrowing_closure(&self, diag: &mut Diag<'_>) { let map = self.infcx.tcx.hir(); let body_id = map.body_owned_by(self.mir_def_id()); diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 2da9bda19e034..134a6baea6e8b 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -3,18 +3,20 @@ use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; +use rustc_data_structures::sync::Lrc; use rustc_expand::base::{ - check_zero_tts, get_single_str_from_tts, parse_expr, resolve_path, DummyResult, ExtCtxt, - MacEager, MacResult, + check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts, parse_expr, + resolve_path, DummyResult, ExtCtxt, MacEager, MacResult, }; use rustc_expand::module::DirOwnership; use rustc_parse::new_parser_from_file; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; +use rustc_span::source_map::SourceMap; use rustc_span::symbol::Symbol; use rustc_span::{Pos, Span}; - use smallvec::SmallVec; +use std::path::{Path, PathBuf}; use std::rc::Rc; // These macros all relate to the file system; they either return @@ -180,32 +182,22 @@ pub fn expand_include_str( tts: TokenStream, ) -> Box { let sp = cx.with_def_site_ctxt(sp); - let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") { - Ok(file) => file, + let (path, path_span) = match get_single_str_spanned_from_tts(cx, sp, tts, "include_str!") { + Ok(res) => res, Err(guar) => return DummyResult::any(sp, guar), }; - let file = match resolve_path(&cx.sess, file.as_str(), sp) { - Ok(f) => f, - Err(err) => { - let guar = err.emit(); - return DummyResult::any(sp, guar); - } - }; - match cx.source_map().load_binary_file(&file) { + match load_binary_file(cx, path.as_str().as_ref(), sp, path_span) { Ok(bytes) => match std::str::from_utf8(&bytes) { Ok(src) => { let interned_src = Symbol::intern(src); MacEager::expr(cx.expr_str(sp, interned_src)) } Err(_) => { - let guar = cx.dcx().span_err(sp, format!("{} wasn't a utf-8 file", file.display())); + let guar = cx.dcx().span_err(sp, format!("`{path}` wasn't a utf-8 file")); DummyResult::any(sp, guar) } }, - Err(e) => { - let guar = cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e)); - DummyResult::any(sp, guar) - } + Err(dummy) => dummy, } } @@ -215,25 +207,123 @@ pub fn expand_include_bytes( tts: TokenStream, ) -> Box { let sp = cx.with_def_site_ctxt(sp); - let file = match get_single_str_from_tts(cx, sp, tts, "include_bytes!") { - Ok(file) => file, + let (path, path_span) = match get_single_str_spanned_from_tts(cx, sp, tts, "include_bytes!") { + Ok(res) => res, Err(guar) => return DummyResult::any(sp, guar), }; - let file = match resolve_path(&cx.sess, file.as_str(), sp) { - Ok(f) => f, + match load_binary_file(cx, path.as_str().as_ref(), sp, path_span) { + Ok(bytes) => { + let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes)); + MacEager::expr(expr) + } + Err(dummy) => dummy, + } +} + +fn load_binary_file( + cx: &mut ExtCtxt<'_>, + original_path: &Path, + macro_span: Span, + path_span: Span, +) -> Result, Box> { + let resolved_path = match resolve_path(&cx.sess, original_path, macro_span) { + Ok(path) => path, Err(err) => { let guar = err.emit(); - return DummyResult::any(sp, guar); + return Err(DummyResult::any(macro_span, guar)); } }; - match cx.source_map().load_binary_file(&file) { - Ok(bytes) => { - let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes)); - MacEager::expr(expr) + match cx.source_map().load_binary_file(&resolved_path) { + Ok(data) => Ok(data), + Err(io_err) => { + let mut err = cx.dcx().struct_span_err( + macro_span, + format!("couldn't read `{}`: {io_err}", resolved_path.display()), + ); + + if original_path.is_relative() { + let source_map = cx.sess.source_map(); + let new_path = source_map + .span_to_filename(macro_span.source_callsite()) + .into_local_path() + .and_then(|src| find_path_suggestion(source_map, src.parent()?, original_path)) + .and_then(|path| path.into_os_string().into_string().ok()); + + if let Some(new_path) = new_path { + err.span_suggestion( + path_span, + "there is a file with the same name in a different directory", + format!("\"{}\"", new_path.escape_debug()), + rustc_lint_defs::Applicability::MachineApplicable, + ); + } + } + let guar = err.emit(); + Err(DummyResult::any(macro_span, guar)) } - Err(e) => { - let guar = cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e)); - DummyResult::any(sp, guar) + } +} + +fn find_path_suggestion( + source_map: &SourceMap, + base_dir: &Path, + wanted_path: &Path, +) -> Option { + // Fix paths that assume they're relative to cargo manifest dir + let mut base_c = base_dir.components(); + let mut wanted_c = wanted_path.components(); + let mut without_base = None; + while let Some(wanted_next) = wanted_c.next() { + if wanted_c.as_path().file_name().is_none() { + break; + } + // base_dir may be absolute + while let Some(base_next) = base_c.next() { + if base_next == wanted_next { + without_base = Some(wanted_c.as_path()); + break; + } + } + } + let root_absolute = without_base.into_iter().map(PathBuf::from); + + let base_dir_components = base_dir.components().count(); + // Avoid going all the way to the root dir + let max_parent_components = if base_dir.is_relative() { + base_dir_components + 1 + } else { + base_dir_components.saturating_sub(1) + }; + + // Try with additional leading ../ + let mut prefix = PathBuf::new(); + let add = std::iter::from_fn(|| { + prefix.push(".."); + Some(prefix.join(wanted_path)) + }) + .take(max_parent_components.min(3)); + + // Try without leading directories + let mut trimmed_path = wanted_path; + let remove = std::iter::from_fn(|| { + let mut components = trimmed_path.components(); + let removed = components.next()?; + trimmed_path = components.as_path(); + let _ = trimmed_path.file_name()?; // ensure there is a file name left + Some([ + Some(trimmed_path.to_path_buf()), + (removed != std::path::Component::ParentDir) + .then(|| Path::new("..").join(trimmed_path)), + ]) + }) + .flatten() + .flatten() + .take(4); + + for new_path in root_absolute.chain(add).chain(remove) { + if source_map.file_exists(&base_dir.join(&new_path)) { + return Some(new_path); } } + None } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index f7ff36c0467d2..9ae82d4845e16 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -94,8 +94,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } if let OperandValue::Immediate(v) = cg_elem.val { - let zero = bx.const_usize(0); - let start = dest.project_index(bx, zero).llval; + let start = dest.llval; let size = bx.const_usize(dest.layout.size.bytes()); // Use llvm.memset.p0i8.* to initialize all zero arrays diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 1107b894ab3ee..15720f25c5cff 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -62,6 +62,7 @@ impl<'tcx> NonConstOp<'tcx> for FloatingPointOp { } } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { feature_err( &ccx.tcx.sess, @@ -556,6 +557,7 @@ impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref { Status::Unstable(sym::const_mut_refs) } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { feature_err( &ccx.tcx.sess, @@ -589,6 +591,7 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess { } } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { let mut err = feature_err( &ccx.tcx.sess, @@ -632,6 +635,7 @@ pub mod ty { } } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { feature_err( &ccx.tcx.sess, diff --git a/compiler/rustc_driver_impl/src/args.rs b/compiler/rustc_driver_impl/src/args.rs index 5dfd37a6da4de..8b6fb5fd660f3 100644 --- a/compiler/rustc_driver_impl/src/args.rs +++ b/compiler/rustc_driver_impl/src/args.rs @@ -97,6 +97,7 @@ impl Expander { /// **Note:** This function doesn't interpret argument 0 in any special way. /// If this function is intended to be used with command line arguments, /// `argv[0]` must be removed prior to calling it manually. +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec { let mut expander = Expander::default(); for arg in at_args { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 85be97ea24fdf..3b6bf0005a3b1 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -4,6 +4,7 @@ //! //! This API is completely unstable and subject to change. +#![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(rustdoc_internals)] diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 5390dda9b21ad..c186d5b284fc4 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -598,6 +598,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// /// In the meantime, though, callsites are required to deal with the "bug" /// locally in whichever way makes the most sense. + #[rustc_lint_diagnostics] #[track_caller] pub fn downgrade_to_delayed_bug(&mut self) { assert!( @@ -631,6 +632,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_span_labels, /// Labels all the given spans with the provided label. /// See [`Self::span_label()`] for more information. + #[rustc_lint_diagnostics] pub fn span_labels(&mut self, spans: impl IntoIterator, label: &str) -> &mut Self { for span in spans { self.span_label(span, label.to_string()); @@ -638,6 +640,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } } + #[rustc_lint_diagnostics] pub fn replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self { let before = self.span.clone(); self.span(after); @@ -653,6 +656,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + #[rustc_lint_diagnostics] pub fn note_expected_found( &mut self, expected_label: &dyn fmt::Display, @@ -663,6 +667,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"") } + #[rustc_lint_diagnostics] pub fn note_expected_found_extra( &mut self, expected_label: &dyn fmt::Display, @@ -705,6 +710,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + #[rustc_lint_diagnostics] pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self { self.highlighted_note(vec![ StringPart::normal(format!("`{name}` from trait: `")), @@ -722,12 +728,14 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } } + #[rustc_lint_diagnostics] fn highlighted_note(&mut self, msg: Vec) -> &mut Self { self.sub_with_highlights(Level::Note, msg, MultiSpan::new()); self } /// This is like [`Diag::note()`], but it's only printed once. + #[rustc_lint_diagnostics] pub fn note_once(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::OnceNote, msg, MultiSpan::new()); self @@ -748,6 +756,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// Prints the span with a note above it. /// This is like [`Diag::note_once()`], but it gets its own span. + #[rustc_lint_diagnostics] pub fn span_note_once>( &mut self, sp: S, @@ -786,12 +795,14 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { } } /// This is like [`Diag::help()`], but it's only printed once. + #[rustc_lint_diagnostics] pub fn help_once(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::OnceHelp, msg, MultiSpan::new()); self } /// Add a help message attached to this diagnostic with a customizable highlighted message. + #[rustc_lint_diagnostics] pub fn highlighted_help(&mut self, msg: Vec) -> &mut Self { self.sub_with_highlights(Level::Help, msg, MultiSpan::new()); self @@ -812,12 +823,14 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// Disallow attaching suggestions this diagnostic. /// Any suggestions attached e.g. with the `span_suggestion_*` methods /// (before and after the call to `disable_suggestions`) will be ignored. + #[rustc_lint_diagnostics] pub fn disable_suggestions(&mut self) -> &mut Self { self.suggestions = Err(SuggestionsDisabled); self } /// Helper for pushing to `self.suggestions`, if available (not disable). + #[rustc_lint_diagnostics] fn push_suggestion(&mut self, suggestion: CodeSuggestion) { for subst in &suggestion.substitutions { for part in &subst.parts { @@ -838,6 +851,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_multipart_suggestion, /// Show a suggestion that has multiple parts to it. /// In other words, multiple changes need to be applied as part of this suggestion. + #[rustc_lint_diagnostics] pub fn multipart_suggestion( &mut self, msg: impl Into, @@ -854,6 +868,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic. /// In other words, multiple changes need to be applied as part of this suggestion. + #[rustc_lint_diagnostics] pub fn multipart_suggestion_verbose( &mut self, msg: impl Into, @@ -869,6 +884,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { } /// [`Diag::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. + #[rustc_lint_diagnostics] pub fn multipart_suggestion_with_style( &mut self, msg: impl Into, @@ -911,6 +927,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// be from the message, showing the span label inline would be visually unpleasant /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't /// improve understandability. + #[rustc_lint_diagnostics] pub fn tool_only_multipart_suggestion( &mut self, msg: impl Into, @@ -943,6 +960,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// * may contain a name of a function, variable, or type, but not whole expressions /// /// See `CodeSuggestion` for more information. + #[rustc_lint_diagnostics] pub fn span_suggestion( &mut self, sp: Span, @@ -961,6 +979,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { } } /// [`Diag::span_suggestion()`] but you can set the [`SuggestionStyle`]. + #[rustc_lint_diagnostics] pub fn span_suggestion_with_style( &mut self, sp: Span, @@ -986,6 +1005,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_span_suggestion_verbose, /// Always show the suggested change. + #[rustc_lint_diagnostics] pub fn span_suggestion_verbose( &mut self, sp: Span, @@ -1006,6 +1026,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_span_suggestions, /// Prints out a message with multiple suggested edits of the code. /// See also [`Diag::span_suggestion()`]. + #[rustc_lint_diagnostics] pub fn span_suggestions( &mut self, sp: Span, @@ -1022,6 +1043,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { ) } } + #[rustc_lint_diagnostics] pub fn span_suggestions_with_style( &mut self, sp: Span, @@ -1052,6 +1074,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// Prints out a message with multiple suggested edits of the code, where each edit consists of /// multiple parts. /// See also [`Diag::multipart_suggestion()`]. + #[rustc_lint_diagnostics] pub fn multipart_suggestions( &mut self, msg: impl Into, @@ -1098,6 +1121,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// inline, it will only show the message and not the suggestion. /// /// See `CodeSuggestion` for more information. + #[rustc_lint_diagnostics] pub fn span_suggestion_short( &mut self, sp: Span, @@ -1121,6 +1145,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// be from the message, showing the span label inline would be visually unpleasant /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't /// improve understandability. + #[rustc_lint_diagnostics] pub fn span_suggestion_hidden( &mut self, sp: Span, @@ -1165,6 +1190,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages /// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of /// interpolated variables). + #[rustc_lint_diagnostics] pub fn subdiagnostic( &mut self, dcx: &crate::DiagCtxt, @@ -1180,6 +1206,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_span, /// Add a span. + #[rustc_lint_diagnostics] pub fn span(&mut self, sp: impl Into) -> &mut Self { self.span = sp.into(); if let Some(span) = self.span.primary_span() { @@ -1188,6 +1215,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } } + #[rustc_lint_diagnostics] pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self { self.is_lint = Some(IsLint { name, has_future_breakage }); self @@ -1195,6 +1223,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_code, /// Add an error code. + #[rustc_lint_diagnostics] pub fn code(&mut self, code: ErrCode) -> &mut Self { self.code = Some(code); self @@ -1202,6 +1231,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_primary_message, /// Add a primary message. + #[rustc_lint_diagnostics] pub fn primary_message(&mut self, msg: impl Into) -> &mut Self { self.messages[0] = (msg.into(), Style::NoStyle); self @@ -1209,6 +1239,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_arg, /// Add an argument. + #[rustc_lint_diagnostics] pub fn arg( &mut self, name: impl Into, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 0caa8fa9e8abe..76b44f73f47b8 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1101,32 +1101,36 @@ impl DiagCtxt { // Functions beginning with `struct_`/`create_` create a diagnostic. Other // functions create and emit a diagnostic all in one go. impl DiagCtxt { - // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't + // user-facing. #[track_caller] - pub fn struct_bug(&self, msg: impl Into) -> Diag<'_, BugAbort> { - Diag::new(self, Bug, msg) + pub fn struct_bug(&self, msg: impl Into>) -> Diag<'_, BugAbort> { + Diag::new(self, Bug, msg.into()) } - // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't + // user-facing. #[track_caller] - pub fn bug(&self, msg: impl Into) -> ! { + pub fn bug(&self, msg: impl Into>) -> ! { self.struct_bug(msg).emit() } - // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't + // user-facing. #[track_caller] pub fn struct_span_bug( &self, span: impl Into, - msg: impl Into, + msg: impl Into>, ) -> Diag<'_, BugAbort> { self.struct_bug(msg).with_span(span) } - // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't + // user-facing. #[track_caller] - pub fn span_bug(&self, span: impl Into, msg: impl Into) -> ! { - self.struct_span_bug(span, msg).emit() + pub fn span_bug(&self, span: impl Into, msg: impl Into>) -> ! { + self.struct_span_bug(span, msg.into()).emit() } #[track_caller] @@ -1240,24 +1244,28 @@ impl DiagCtxt { } /// Ensures that an error is printed. See `Level::DelayedBug`. - // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + // + // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't + // user-facing. #[track_caller] - pub fn delayed_bug(&self, msg: impl Into) -> ErrorGuaranteed { - Diag::::new(self, DelayedBug, msg).emit() + pub fn delayed_bug(&self, msg: impl Into>) -> ErrorGuaranteed { + Diag::::new(self, DelayedBug, msg.into()).emit() } /// Ensures that an error is printed. See `Level::DelayedBug`. /// /// Note: this function used to be called `delay_span_bug`. It was renamed /// to match similar functions like `span_err`, `span_warn`, etc. - // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + // + // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't + // user-facing. #[track_caller] pub fn span_delayed_bug( &self, sp: impl Into, - msg: impl Into, + msg: impl Into>, ) -> ErrorGuaranteed { - Diag::::new(self, DelayedBug, msg).with_span(sp).emit() + Diag::::new(self, DelayedBug, msg.into()).with_span(sp).emit() } #[rustc_lint_diagnostics] diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 0beba2a550466..58589ebdca675 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1233,21 +1233,18 @@ pub fn resolve_path(sess: &Session, path: impl Into, span: Span) -> PRe // after macro expansion (that is, they are unhygienic). if !path.is_absolute() { let callsite = span.source_callsite(); - let mut result = match sess.source_map().span_to_filename(callsite) { - FileName::Real(name) => name - .into_local_path() - .expect("attempting to resolve a file path in an external file"), - FileName::DocTest(path, _) => path, - other => { - return Err(sess.dcx().create_err(errors::ResolveRelativePath { - span, - path: sess.source_map().filename_for_diagnostics(&other).to_string(), - })); - } + let source_map = sess.source_map(); + let Some(mut base_path) = source_map.span_to_filename(callsite).into_local_path() else { + return Err(sess.dcx().create_err(errors::ResolveRelativePath { + span, + path: source_map + .filename_for_diagnostics(&source_map.span_to_filename(callsite)) + .to_string(), + })); }; - result.pop(); - result.push(path); - Ok(result) + base_path.pop(); + base_path.push(path); + Ok(base_path) } else { Ok(path) } @@ -1344,6 +1341,15 @@ pub fn get_single_str_from_tts( tts: TokenStream, name: &str, ) -> Result { + get_single_str_spanned_from_tts(cx, span, tts, name).map(|(s, _)| s) +} + +pub fn get_single_str_spanned_from_tts( + cx: &mut ExtCtxt<'_>, + span: Span, + tts: TokenStream, + name: &str, +) -> Result<(Symbol, Span), ErrorGuaranteed> { let mut p = cx.new_parser_from_tts(tts); if p.token == token::Eof { let guar = cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); @@ -1355,7 +1361,12 @@ pub fn get_single_str_from_tts( if p.token != token::Eof { cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); } - expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s) + expr_to_spanned_string(cx, ret, "argument must be a string literal") + .map_err(|err| match err { + Ok((err, _)) => err.emit(), + Err(guar) => guar, + }) + .map(|(symbol, _style, span)| (symbol, span)) } /// Extracts comma-separated expressions from `tts`. @@ -1493,6 +1504,8 @@ fn pretty_printing_compatibility_hack(item: &Item, sess: &Session) -> bool { }; if crate_matches { + // FIXME: make this translatable + #[allow(rustc::untranslatable_diagnostic)] sess.psess.buffer_lint_with_diagnostic( PROC_MACRO_BACK_COMPAT, item.ident.span, diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 921fea143124a..c95d7cdeb73ab 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -239,6 +239,7 @@ impl<'a> StripUnconfigured<'a> { /// Gives a compiler warning when the `cfg_attr` contains no attributes and /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub(crate) fn expand_cfg_attr(&self, attr: &Attribute, recursive: bool) -> Vec { let Some((cfg_predicate, expanded_attrs)) = rustc_parse::parse_cfg_attr(attr, &self.sess.psess) @@ -273,6 +274,7 @@ impl<'a> StripUnconfigured<'a> { } } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn expand_cfg_attr_item( &self, attr: &Attribute, @@ -371,6 +373,7 @@ impl<'a> StripUnconfigured<'a> { } /// If attributes are not allowed on expressions, emit an error for `attr` + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable #[instrument(level = "trace", skip(self))] pub(crate) fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { if self.features.is_some_and(|features| !features.stmt_expr_attributes) @@ -384,7 +387,6 @@ impl<'a> StripUnconfigured<'a> { ); if attr.is_doc_comment() { - #[allow(rustc::untranslatable_diagnostic)] err.help("`///` is for documentation comments. For a plain comment, use `//`."); } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 169973200aafa..fcc439e71f95b 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -783,6 +783,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }) } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) { let kind = match item { Annotatable::Item(_) @@ -825,6 +826,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } impl<'ast, 'a> Visitor<'ast> for GateProcMacroInput<'a> { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn visit_item(&mut self, item: &'ast ast::Item) { match &item.kind { ItemKind::Mod(_, mod_kind) @@ -1689,6 +1691,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // Detect use of feature-gated or invalid attributes on macro invocations // since they will not be detected after macro expansion. + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) { let features = self.cx.ecfg.features; let mut attrs = attrs.iter().peekable(); diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 0e73abc9ed854..e550f7242c338 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -5,6 +5,7 @@ #![feature(associated_type_defaults)] #![feature(if_let_guard)] #![feature(let_chains)] +#![feature(lint_reasons)] #![feature(macro_metavar_expr)] #![feature(map_try_insert)] #![feature(proc_macro_diagnostic)] diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 32a083a72f013..ddc685c9d07da 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -517,6 +517,9 @@ impl server::FreeFunctions for Rustc<'_, '_> { Diag::new(&self.psess().dcx, diagnostic.level.to_internal(), message); diag.span(MultiSpan::from_spans(diagnostic.spans)); for child in diagnostic.children { + // This message comes from another diagnostic, and we are just reconstructing the + // diagnostic, so there's no need for translation. + #[allow(rustc::untranslatable_diagnostic)] diag.sub(child.level.to_internal(), child.message, MultiSpan::from_spans(child.spans)); } diag.emit(); diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 8c47b7594535c..a3510dc9bff4d 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -178,6 +178,7 @@ impl Write for Shared { } } +#[allow(rustc::untranslatable_diagnostic)] // no translation needed for tests fn test_harness(file_text: &str, span_labels: Vec, expected_output: &str) { create_default_session_globals_then(|| { let (handler, source_map, output) = create_test_handler(); @@ -192,7 +193,6 @@ fn test_harness(file_text: &str, span_labels: Vec, expected_output: & println!("text: {:?}", source_map.span_to_snippet(span)); } - #[allow(rustc::untranslatable_diagnostic)] handler.span_err(msp, "foo"); assert!( diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 3f971aad04242..0d51e1e46e06a 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -695,8 +695,8 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Used by the `rustc::potential_query_instability` lint to warn methods which // might not be stable during incremental compilation. rustc_attr!(rustc_lint_query_instability, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), - // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints - // to assist in changes to diagnostic APIs. + // Used by the `rustc::diagnostic_outside_of_impl` lints to assist in changes to diagnostic + // APIs. Any function with this attribute will be checked by that lint. rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions` // types (as well as any others in future). diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index b2ec8e5342924..284d965979ece 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -320,6 +320,7 @@ pub struct Config { // JUSTIFICATION: before session exists, only config #[allow(rustc::bad_opt_access)] +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { trace!("run_compiler"); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 5e1fc248b801c..4f15d1c1d3a0b 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -590,7 +590,7 @@ fn test_codegen_options_tracking_hash() { tracked!(force_frame_pointers, Some(false)); tracked!(force_unwind_tables, Some(true)); tracked!(inline_threshold, Some(0xf007ba11)); - tracked!(instrument_coverage, InstrumentCoverage::All); + tracked!(instrument_coverage, InstrumentCoverage::Yes); tracked!(link_dead_code, Some(true)); tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); tracked!(llvm_args, vec![String::from("1"), String::from("2")]); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index bf3beb677aa6d..0d50200133cb2 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -160,6 +160,7 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( }) } +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBackendFn { match unsafe { load_symbol_from_dylib::(path, "__rustc_codegen_backend") } { Ok(backend_sym) => backend_sym, @@ -227,6 +228,7 @@ fn get_rustc_path_inner(bin_path: &str) -> Option { }) } +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn get_codegen_sysroot( early_dcx: &EarlyDiagCtxt, maybe_sysroot: &Option, @@ -319,6 +321,7 @@ fn get_codegen_sysroot( } } +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub(crate) fn check_attr_crate_type( sess: &Session, attrs: &[ast::Attribute], diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index f043a312c719c..89cdde11726ab 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -621,12 +621,13 @@ pub trait LintContext { /// Note that this function should only be called for [`LintExpectationId`]s /// retrieved from the current lint pass. Buffered or manually created ids can /// cause ICEs. - #[rustc_lint_diagnostics] fn fulfill_expectation(&self, expectation: LintExpectationId) { // We need to make sure that submitted expectation ids are correctly fulfilled suppressed // and stored between compilation sessions. To not manually do these steps, we simply create - // a dummy diagnostic and emit is as usual, which will be suppressed and stored like a normal - // expected lint diagnostic. + // a dummy diagnostic and emit it as usual, which will be suppressed and stored like a + // normal expected lint diagnostic. + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] self.sess() .dcx() .struct_expect( diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index fdcd0192e6a74..3a8c8e79b4f28 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -10,7 +10,7 @@ use rustc_ast as ast; use rustc_hir::def::Res; use rustc_hir::{def_id::DefId, Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath}; use rustc_hir::{BinOp, BinOpKind, HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind}; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty as MiddleTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::symbol::{kw, sym, Symbol}; @@ -338,10 +338,11 @@ impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword { } declare_tool_lint! { - /// The `untranslatable_diagnostic` lint detects diagnostics created - /// without using translatable Fluent strings. + /// The `untranslatable_diagnostic` lint detects messages passed to functions with `impl + /// Into<{D,Subd}iagMessage` parameters without using translatable Fluent strings. /// - /// More details on translatable diagnostics can be found [here](https://rustc-dev-guide.rust-lang.org/diagnostics/translation.html). + /// More details on translatable diagnostics can be found + /// [here](https://rustc-dev-guide.rust-lang.org/diagnostics/translation.html). pub rustc::UNTRANSLATABLE_DIAGNOSTIC, Deny, "prevent creation of diagnostics which cannot be translated", @@ -349,11 +350,13 @@ declare_tool_lint! { } declare_tool_lint! { - /// The `diagnostic_outside_of_impl` lint detects diagnostics created manually, - /// and inside an `IntoDiagnostic`/`AddToDiagnostic` implementation, - /// or a `#[derive(Diagnostic)]`/`#[derive(Subdiagnostic)]` expansion. + /// The `diagnostic_outside_of_impl` lint detects calls to functions annotated with + /// `#[rustc_lint_diagnostics]` that are outside an `IntoDiagnostic`, `AddToDiagnostic`, or + /// `DecorateLint` impl, or a `#[derive(Diagnostic)]`, `#[derive(Subdiagnostic)]`, + /// `#[derive(DecorateLint)]` expansion. /// - /// More details on diagnostics implementations can be found [here](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html). + /// More details on diagnostics implementations can be found + /// [here](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html). pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL, Deny, "prevent creation of diagnostics outside of `IntoDiagnostic`/`AddToDiagnostic` impls", @@ -364,54 +367,130 @@ declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE impl LateLintPass<'_> for Diagnostics { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return }; - debug!(?span, ?def_id, ?args); - let has_attr = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, args) + // Only check function calls and method calls. + let (span, def_id, fn_gen_args, call_tys) = match expr.kind { + ExprKind::Call(callee, args) => { + match cx.typeck_results().node_type(callee.hir_id).kind() { + &ty::FnDef(def_id, fn_gen_args) => { + let call_tys: Vec<_> = + args.iter().map(|arg| cx.typeck_results().expr_ty(arg)).collect(); + (callee.span, def_id, fn_gen_args, call_tys) + } + _ => return, // occurs for fns passed as args + } + } + ExprKind::MethodCall(segment, _recv, args, _span) => { + let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); + let fn_gen_args = cx.typeck_results().node_args(expr.hir_id); + let mut call_tys: Vec<_> = + args.iter().map(|arg| cx.typeck_results().expr_ty(arg)).collect(); + call_tys.insert(0, cx.tcx.types.self_param); // dummy inserted for `self` + (segment.ident.span, def_id, fn_gen_args, call_tys) + } + _ => return, + }; + + // Is the callee marked with `#[rustc_lint_diagnostics]`? + let has_attr = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, fn_gen_args) .ok() .flatten() .is_some_and(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics)); - if !has_attr { - return; - } - let mut found_parent_with_attr = false; - let mut found_impl = false; - for (hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) { - if let Some(owner_did) = hir_id.as_owner() { - found_parent_with_attr = found_parent_with_attr - || cx.tcx.has_attr(owner_did, sym::rustc_lint_diagnostics); + // Closure: is the type `{D,Subd}iagMessage`? + let is_diag_message = |ty: MiddleTy<'_>| { + if let Some(adt_def) = ty.ty_adt_def() + && let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did()) + && matches!(name, sym::DiagMessage | sym::SubdiagMessage) + { + true + } else { + false } + }; - debug!(?parent); - if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent - && let Impl { of_trait: Some(of_trait), .. } = impl_ - && let Some(def_id) = of_trait.trait_def_id() - && let Some(name) = cx.tcx.get_diagnostic_name(def_id) - && matches!(name, sym::IntoDiagnostic | sym::AddToDiagnostic | sym::DecorateLint) - { - found_impl = true; - break; + // Does the callee have a `impl Into<{D,Subd}iagMessage>` parameter? (There should be at + // most one.) + let mut impl_into_diagnostic_message_param = None; + let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates; + for (i, ¶m_ty) in fn_sig.inputs().iter().enumerate() { + if let ty::Param(p) = param_ty.kind() { + // It is a type parameter. Check if it is `impl Into<{D,Subd}iagMessage>`. + for pred in predicates.iter() { + if let Some(trait_pred) = pred.as_trait_clause() + && let trait_ref = trait_pred.skip_binder().trait_ref + && trait_ref.self_ty() == param_ty // correct predicate for the param? + && cx.tcx.is_diagnostic_item(sym::Into, trait_ref.def_id) + && let ty1 = trait_ref.args.type_at(1) + && is_diag_message(ty1) + { + if impl_into_diagnostic_message_param.is_some() { + cx.tcx.dcx().span_bug( + span, + "can't handle multiple `impl Into<{D,Sub}iagMessage>` params", + ); + } + impl_into_diagnostic_message_param = Some((i, p.name)); + } + } } } - debug!(?found_impl); - if !found_parent_with_attr && !found_impl { - cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl); + + // Is the callee interesting? + if !has_attr && impl_into_diagnostic_message_param.is_none() { + return; } - let mut found_diagnostic_message = false; - for ty in args.types() { - debug!(?ty); - if let Some(adt_def) = ty.ty_adt_def() - && let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did()) - && matches!(name, sym::DiagMessage | sym::SubdiagMessage) + // Is the parent method marked with `#[rustc_lint_diagnostics]`? + let mut parent_has_attr = false; + for (hir_id, _parent) in cx.tcx.hir().parent_iter(expr.hir_id) { + if let Some(owner_did) = hir_id.as_owner() + && cx.tcx.has_attr(owner_did, sym::rustc_lint_diagnostics) { - found_diagnostic_message = true; + parent_has_attr = true; break; } } - debug!(?found_diagnostic_message); - if !found_parent_with_attr && !found_diagnostic_message { - cx.emit_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, UntranslatableDiag); + + // Calls to `#[rustc_lint_diagnostics]`-marked functions should only occur: + // - inside an impl of `IntoDiagnostic`, `AddToDiagnostic`, or `DecorateLint`, or + // - inside a parent function that is itself marked with `#[rustc_lint_diagnostics]`. + // + // Otherwise, emit a `DIAGNOSTIC_OUTSIDE_OF_IMPL` lint. + if has_attr && !parent_has_attr { + let mut is_inside_appropriate_impl = false; + for (_hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) { + debug!(?parent); + if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent + && let Impl { of_trait: Some(of_trait), .. } = impl_ + && let Some(def_id) = of_trait.trait_def_id() + && let Some(name) = cx.tcx.get_diagnostic_name(def_id) + && matches!( + name, + sym::IntoDiagnostic | sym::AddToDiagnostic | sym::DecorateLint + ) + { + is_inside_appropriate_impl = true; + break; + } + } + debug!(?is_inside_appropriate_impl); + if !is_inside_appropriate_impl { + cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl); + } + } + + // Calls to methods with an `impl Into<{D,Subd}iagMessage>` parameter must be passed an arg + // with type `{D,Subd}iagMessage` or `impl Into<{D,Subd}iagMessage>`. Otherwise, emit an + // `UNTRANSLATABLE_DIAGNOSTIC` lint. + if let Some((param_i, param_i_p_name)) = impl_into_diagnostic_message_param { + // Is the arg type `{Sub,D}iagMessage`or `impl Into<{Sub,D}iagMessage>`? + let arg_ty = call_tys[param_i]; + let is_translatable = is_diag_message(arg_ty) + || matches!(arg_ty.kind(), ty::Param(p) if p.name == param_i_p_name); + if !is_translatable { + cx.emit_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, UntranslatableDiag); + } } } } @@ -425,7 +504,7 @@ declare_tool_lint! { report_in_external_macro: true } -declare_lint_pass!(BadOptAccess => [ BAD_OPT_ACCESS ]); +declare_lint_pass!(BadOptAccess => [BAD_OPT_ACCESS]); impl LateLintPass<'_> for BadOptAccess { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index c552b9832553a..de642f373b5c4 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -104,6 +104,7 @@ const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [ ]; impl<'tcx> LateLintPass<'tcx> for LetUnderscore { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) { if matches!(local.source, rustc_hir::LocalSource::AsyncFn) { return; diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 906cd1d1283f8..2f08cd53b75a1 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -724,6 +724,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { }; } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: Option) { let sess = self.sess; for (attr_index, attr) in attrs.iter().enumerate() { diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 0d37c0feb0c24..72757d90e4238 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -947,6 +947,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { } } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn report_unused_deps(&mut self, krate: &ast::Crate) { // Make a point span rather than covering the whole file let span = krate.spans.inner_span.shrink_to_lo(); diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index cdcc586b09e70..baa2e1ff60215 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -93,6 +93,7 @@ struct Collector<'tcx> { } impl<'tcx> Collector<'tcx> { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn process_module(&mut self, module: &ForeignModule) { let ForeignModule { def_id, abi, ref foreign_items } = *module; let def_id = def_id.expect_local(); diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index dda41f70f067a..b1bfd2f1105a8 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -17,6 +17,7 @@ use rustc_target::abi::call::FnAbi; use rustc_target::abi::*; use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target}; +use std::borrow::Cow; use std::cmp; use std::fmt; use std::num::NonZero; @@ -268,7 +269,7 @@ pub struct LayoutCx<'tcx, C> { impl<'tcx> LayoutCalculator for LayoutCx<'tcx, TyCtxt<'tcx>> { type TargetDataLayoutRef = &'tcx TargetDataLayout; - fn delayed_bug(&self, txt: String) { + fn delayed_bug(&self, txt: impl Into>) { self.tcx.dcx().delayed_bug(txt); } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index a0baaa3ce2832..427c0f04bd11e 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -13,7 +13,7 @@ use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use crate::ty::{List, ParamEnv}; use hir::def::DefKind; use rustc_data_structures::captures::Captures; -use rustc_errors::{DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagnosticArg, MultiSpan}; +use rustc_errors::{DiagArgValue, ErrorGuaranteed, IntoDiagnosticArg, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; @@ -1543,7 +1543,7 @@ impl<'tcx> Ty<'tcx> { pub fn new_error_with_message>( tcx: TyCtxt<'tcx>, span: S, - msg: impl Into, + msg: impl Into>, ) -> Ty<'tcx> { let reported = tcx.dcx().span_delayed_bug(span, msg); Ty::new(tcx, Error(reported)) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 04c0cf7436e2b..a99db9cb8cc58 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1074,6 +1074,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// of one item. Read the documentation of [`check_doc_inline`] for more information. /// /// [`check_doc_inline`]: Self::check_doc_inline + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn check_doc_attrs( &self, attr: &Attribute, @@ -1756,6 +1757,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if the `#[repr]` attributes on `item` are valid. + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn check_repr( &self, attrs: &[Attribute], @@ -2328,6 +2330,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id); if let Some(hir_sig) = hir_sig { + #[allow(rustc::diagnostic_outside_of_impl)] // FIXME match terr { TypeError::ArgumentMutability(idx) | TypeError::ArgumentSorts(_, idx) => { if let Some(ty) = hir_sig.decl.inputs.get(idx) { diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 02792491f5ed0..30a65d69c4ef8 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -77,6 +77,7 @@ impl<'tcx> CheckConstVisitor<'tcx> { } /// Emits an error when an unsupported expression is found in a const context. + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn const_check_violated(&self, expr: NonConstExpr, span: Span) { let Self { tcx, def_id, const_kind } = *self; diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 97c70e327f0fe..5246389248e9f 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -114,6 +114,7 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { } } +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> { if let Some((def_id, _)) = visitor.start_fn { Some((def_id.to_def_id(), EntryFnType::Start)) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 833ebfa03e5fc..0bb2a69ae9926 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1736,7 +1736,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'a>) { - let PrivacyError { ident, binding, outermost_res, parent_scope, dedup_span } = + let PrivacyError { ident, binding, outermost_res, parent_scope, single_nested, dedup_span } = *privacy_error; let res = binding.res(); @@ -1775,7 +1775,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &import_suggestions, Instead::Yes, FoundUse::Yes, - DiagMode::Import, + DiagMode::Import { append: single_nested }, vec![], "", ); @@ -2701,7 +2701,11 @@ pub(crate) enum DiagMode { /// The binding is part of a pattern Pattern, /// The binding is part of a use statement - Import, + Import { + /// `true` mean add the tips afterward for case `use a::{b,c}`, + /// rather than replacing within. + append: bool, + }, } pub(crate) fn import_candidates( @@ -2726,6 +2730,8 @@ pub(crate) fn import_candidates( ); } +type PathString<'a> = (String, &'a str, Option, &'a Option, bool); + /// When an entity with a given name is not available in scope, we search for /// entities with that name in all crates. This method allows outputting the /// results of this search in a programmer-friendly way. If any entities are @@ -2746,10 +2752,8 @@ fn show_candidates( return false; } - let mut accessible_path_strings: Vec<(String, &str, Option, &Option, bool)> = - Vec::new(); - let mut inaccessible_path_strings: Vec<(String, &str, Option, &Option, bool)> = - Vec::new(); + let mut accessible_path_strings: Vec> = Vec::new(); + let mut inaccessible_path_strings: Vec> = Vec::new(); candidates.iter().for_each(|c| { if c.accessible { @@ -2811,6 +2815,15 @@ fn show_candidates( err.note(note.clone()); } + let append_candidates = |msg: &mut String, accessible_path_strings: Vec>| { + msg.push(':'); + + for candidate in accessible_path_strings { + msg.push('\n'); + msg.push_str(&candidate.0); + } + }; + if let Some(span) = use_placement_span { let (add_use, trailing) = match mode { DiagMode::Pattern => { @@ -2822,7 +2835,7 @@ fn show_candidates( ); return true; } - DiagMode::Import => ("", ""), + DiagMode::Import { .. } => ("", ""), DiagMode::Normal => ("use ", ";\n"), }; for candidate in &mut accessible_path_strings { @@ -2839,13 +2852,22 @@ fn show_candidates( format!("{add_use}{}{append}{trailing}{additional_newline}", &candidate.0); } - err.span_suggestions_with_style( - span, - msg, - accessible_path_strings.into_iter().map(|a| a.0), - Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways, - ); + match mode { + DiagMode::Import { append: true, .. } => { + append_candidates(&mut msg, accessible_path_strings); + err.span_help(span, msg); + } + _ => { + err.span_suggestions_with_style( + span, + msg, + accessible_path_strings.into_iter().map(|a| a.0), + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + } + } + if let [first, .., last] = &path[..] { let sp = first.ident.span.until(last.ident.span); // Our suggestion is empty, so make sure the span is not empty (or we'd ICE). @@ -2860,17 +2882,11 @@ fn show_candidates( } } } else { - msg.push(':'); - - for candidate in accessible_path_strings { - msg.push('\n'); - msg.push_str(&candidate.0); - } - + append_candidates(&mut msg, accessible_path_strings); err.help(msg); } true - } else if !(inaccessible_path_strings.is_empty() || matches!(mode, DiagMode::Import)) { + } else if !(inaccessible_path_strings.is_empty() || matches!(mode, DiagMode::Import { .. })) { let prefix = if let DiagMode::Pattern = mode { "you might have meant to match on " } else { "" }; if let [(name, descr, def_id, note, _)] = &inaccessible_path_strings[..] { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index a9fdddbfab90f..6518d9735ae52 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -868,7 +868,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { .into_iter() .find_map(|binding| if binding == ignore_binding { None } else { binding }); - if let Some(Finalize { path_span, report_private, used, .. }) = finalize { + if let Some(Finalize { path_span, report_private, used, root_span, .. }) = finalize { let Some(binding) = binding else { return Err((Determined, Weak::No)); }; @@ -881,6 +881,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { dedup_span: path_span, outermost_res: None, parent_scope: *parent_scope, + single_nested: path_span != root_span, }); } else { return Err((Determined, Weak::No)); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 6647a9a279285..9e5b2fe094f4c 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -715,7 +715,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &mut diag, Some(err.span), candidates, - DiagMode::Import, + DiagMode::Import { append: false }, (source != target) .then(|| format!(" as {target}")) .as_deref() diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2c80fbdab36d7..83ee1d8bdcb10 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3953,6 +3953,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // Avoid recording definition of `A::B` in `::B::C`. self.r.record_partial_res(node_id, partial_res); self.resolve_elided_lifetimes_in_path(partial_res, path, source, path_span); + self.lint_unused_qualifications(path, ns, finalize); } partial_res @@ -4145,39 +4146,6 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { PathResult::Indeterminate => bug!("indeterminate path result in resolve_qpath"), }; - if path.iter().all(|seg| !seg.ident.span.from_expansion()) { - let end_pos = - path.iter().position(|seg| seg.has_generic_args).map_or(path.len(), |pos| pos + 1); - let unqualified = - path[..end_pos].iter().enumerate().skip(1).rev().find_map(|(i, seg)| { - // Preserve the current namespace for the final path segment, but use the type - // namespace for all preceding segments - // - // e.g. for `std::env::args` check the `ValueNS` for `args` but the `TypeNS` for - // `std` and `env` - // - // If the final path segment is beyond `end_pos` all the segments to check will - // use the type namespace - let ns = if i + 1 == path.len() { ns } else { TypeNS }; - let res = self.r.partial_res_map.get(&seg.id?)?.full_res()?; - let binding = self.resolve_ident_in_lexical_scope(seg.ident, ns, None, None)?; - - (res == binding.res()).then_some(seg) - }); - - if let Some(unqualified) = unqualified { - self.r.lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::UNUSED_QUALIFICATIONS, - finalize.node_id, - finalize.path_span, - "unnecessary qualification", - lint::BuiltinLintDiag::UnusedQualifications { - removal_span: finalize.path_span.until(unqualified.ident.span), - }, - ); - } - } - Ok(Some(result)) } @@ -4656,6 +4624,42 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { self.r.doc_link_traits_in_scope = doc_link_traits_in_scope; } } + + fn lint_unused_qualifications(&mut self, path: &[Segment], ns: Namespace, finalize: Finalize) { + if path.iter().any(|seg| seg.ident.span.from_expansion()) { + return; + } + + let end_pos = + path.iter().position(|seg| seg.has_generic_args).map_or(path.len(), |pos| pos + 1); + let unqualified = path[..end_pos].iter().enumerate().skip(1).rev().find_map(|(i, seg)| { + // Preserve the current namespace for the final path segment, but use the type + // namespace for all preceding segments + // + // e.g. for `std::env::args` check the `ValueNS` for `args` but the `TypeNS` for + // `std` and `env` + // + // If the final path segment is beyond `end_pos` all the segments to check will + // use the type namespace + let ns = if i + 1 == path.len() { ns } else { TypeNS }; + let res = self.r.partial_res_map.get(&seg.id?)?.full_res()?; + let binding = self.resolve_ident_in_lexical_scope(seg.ident, ns, None, None)?; + + (res == binding.res()).then_some(seg) + }); + + if let Some(unqualified) = unqualified { + self.r.lint_buffer.buffer_lint_with_diagnostic( + lint::builtin::UNUSED_QUALIFICATIONS, + finalize.node_id, + finalize.path_span, + "unnecessary qualification", + lint::BuiltinLintDiag::UnusedQualifications { + removal_span: path[0].ident.span.until(unqualified.ident.span), + }, + ); + } + } } /// Walks the whole crate in DFS order, visiting each item, counting the declared number of diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 0434ff171934e..5de147c2b2444 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -729,6 +729,8 @@ struct PrivacyError<'a> { dedup_span: Span, outermost_res: Option<(Res, Ident)>, parent_scope: ParentScope<'a>, + /// Is the format `use a::{b,c}`? + single_nested: bool, } #[derive(Debug)] diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index dadbcff6f68cd..61a220428b03b 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1,6 +1,8 @@ //! Contains infrastructure for configuring the compiler, including parsing //! command-line options. +#![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable + pub use crate::options::*; use crate::errors::FileWriteFail; @@ -146,8 +148,10 @@ pub enum LtoCli { /// unless the function has type parameters. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum InstrumentCoverage { - /// Default `-C instrument-coverage` or `-C instrument-coverage=statement` - All, + /// `-C instrument-coverage=no` (or `off`, `false` etc.) + No, + /// `-C instrument-coverage` or `-C instrument-coverage=yes` + Yes, /// Additionally, instrument branches and output branch coverage. /// `-Zunstable-options -C instrument-coverage=branch` Branch, @@ -155,8 +159,6 @@ pub enum InstrumentCoverage { ExceptUnusedGenerics, /// `-Zunstable-options -C instrument-coverage=except-unused-functions` ExceptUnusedFunctions, - /// `-C instrument-coverage=off` (or `no`, etc.) - Off, } /// Settings for `-Z instrument-xray` flag. @@ -2468,9 +2470,7 @@ pub fn parse_externs( )); let adjusted_name = name.replace('-', "_"); if is_ascii_ident(&adjusted_name) { - // FIXME: make this translatable - #[allow(rustc::diagnostic_outside_of_impl)] - #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] // FIXME error.help(format!( "consider replacing the dashes with underscores: `{adjusted_name}`" )); @@ -2722,7 +2722,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M // This is what prevents them from being used on stable compilers. match cg.instrument_coverage { // Stable values: - InstrumentCoverage::All | InstrumentCoverage::Off => {} + InstrumentCoverage::Yes | InstrumentCoverage::No => {} // Unstable values: InstrumentCoverage::Branch | InstrumentCoverage::ExceptUnusedFunctions @@ -2736,7 +2736,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M } } - if cg.instrument_coverage != InstrumentCoverage::Off { + if cg.instrument_coverage != InstrumentCoverage::No { if cg.profile_generate.enabled() || cg.profile_use.is_some() { early_dcx.early_fatal( "option `-C instrument-coverage` is not compatible with either `-C profile-use` \ diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 55e1556cdf8d6..93bef82e4ba15 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -320,6 +320,7 @@ macro_rules! redirect_field { type OptionSetter = fn(&mut O, v: Option<&str>) -> bool; type OptionDescrs = &'static [(&'static str, OptionSetter, &'static str, &'static str)]; +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn build_options( early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches, @@ -394,8 +395,7 @@ mod desc { pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of(); pub const parse_optimization_fuel: &str = "crate=integer"; pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; - pub const parse_instrument_coverage: &str = - "`all` (default), `branch`, `except-unused-generics`, `except-unused-functions`, or `off`"; + pub const parse_instrument_coverage: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc) or (unstable) one of `branch`, `except-unused-generics`, `except-unused-functions`"; pub const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; pub const parse_unpretty: &str = "`string` or `string=string`"; pub const parse_treat_err_as_bug: &str = "either no value or a non-negative number"; @@ -918,18 +918,18 @@ mod parse { if v.is_some() { let mut bool_arg = false; if parse_bool(&mut bool_arg, v) { - *slot = if bool_arg { InstrumentCoverage::All } else { InstrumentCoverage::Off }; + *slot = if bool_arg { InstrumentCoverage::Yes } else { InstrumentCoverage::No }; return true; } } let Some(v) = v else { - *slot = InstrumentCoverage::All; + *slot = InstrumentCoverage::Yes; return true; }; *slot = match v { - "all" => InstrumentCoverage::All, + "all" => InstrumentCoverage::Yes, "branch" => InstrumentCoverage::Branch, "except-unused-generics" | "except_unused_generics" => { InstrumentCoverage::ExceptUnusedGenerics @@ -937,7 +937,7 @@ mod parse { "except-unused-functions" | "except_unused_functions" => { InstrumentCoverage::ExceptUnusedFunctions } - "off" | "no" | "n" | "false" | "0" => InstrumentCoverage::Off, + "0" => InstrumentCoverage::No, _ => return false, }; true @@ -1444,15 +1444,15 @@ options! { inline_threshold: Option = (None, parse_opt_number, [TRACKED], "set the threshold for inlining a function"), #[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")] - instrument_coverage: InstrumentCoverage = (InstrumentCoverage::Off, parse_instrument_coverage, [TRACKED], + instrument_coverage: InstrumentCoverage = (InstrumentCoverage::No, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`); \ implies `-C symbol-mangling-version=v0`. Optional values are: - `=all` (implicit value) - `=branch` - `=except-unused-generics` - `=except-unused-functions` - `=off` (default)"), + `=no` `=n` `=off` `=false` (default) + `=yes` `=y` `=on` `=true` (implicit value) + `=branch` (unstable) + `=except-unused-generics` (unstable) + `=except-unused-functions` (unstable)"), link_arg: (/* redirected to link_args */) = ((), parse_string_push, [UNTRACKED], "a single extra argument to append to the linker invocation (can be used several times)"), link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 506bd5d5dbd81..398138d7e1ff3 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -168,6 +168,7 @@ pub fn add_feature_diagnostics( /// This variant allows you to control whether it is a library or language feature. /// Almost always, you want to use this for a language feature. If so, prefer /// `add_feature_diagnostics`. +#[allow(rustc::diagnostic_outside_of_impl)] // FIXME pub fn add_feature_diagnostics_for_issue( err: &mut Diag<'_, G>, sess: &Session, diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs index 9b913c76998e0..32d5e430717bd 100644 --- a/compiler/rustc_session/src/search_paths.rs +++ b/compiler/rustc_session/src/search_paths.rs @@ -61,6 +61,7 @@ impl SearchPath { (PathKind::All, path) }; if path.is_empty() { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable early_dcx.early_fatal("empty search path given via `-L`"); } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 4f4d8fabb7261..ae1dbd13204b0 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -312,6 +312,7 @@ impl Session { ) -> Diag<'a> { let mut err = self.dcx().create_err(err); if err.code.is_none() { + #[allow(rustc::diagnostic_outside_of_impl)] err.code(E0658); } add_feature_diagnostics(&mut err, self, feature); @@ -352,7 +353,7 @@ impl Session { } pub fn instrument_coverage(&self) -> bool { - self.opts.cg.instrument_coverage() != InstrumentCoverage::Off + self.opts.cg.instrument_coverage() != InstrumentCoverage::No } pub fn instrument_coverage_branch(&self) -> bool { @@ -898,19 +899,6 @@ impl Session { } pub fn should_prefer_remapped_for_codegen(&self) -> bool { - // bail out, if any of the requested crate types aren't: - // "compiled executables or libraries" - for crate_type in &self.opts.crate_types { - match crate_type { - CrateType::Executable - | CrateType::Dylib - | CrateType::Rlib - | CrateType::Staticlib - | CrateType::Cdylib => continue, - CrateType::ProcMacro => return false, - } - } - let has_split_debuginfo = match self.split_debuginfo() { SplitDebuginfo::Off => false, SplitDebuginfo::Packed => true, @@ -1022,6 +1010,7 @@ fn default_emitter( // JUSTIFICATION: literally session construction #[allow(rustc::bad_opt_access)] +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn build_session( early_dcx: EarlyDiagCtxt, sopts: config::Options, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 616a7ccc7c64f..0c974ef4ca3eb 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -427,6 +427,17 @@ impl FileName { src.hash(&mut hasher); FileName::InlineAsm(hasher.finish()) } + + /// Returns the path suitable for reading from the file system on the local host, + /// if this information exists. + /// Avoid embedding this in build artifacts; see `remapped_path_if_available()` for that. + pub fn into_local_path(self) -> Option { + match self { + FileName::Real(path) => path.into_local_path(), + FileName::DocTest(path, _) => Some(path), + _ => None, + } + } } /// Represents a span. diff --git a/library/test/src/formatters/terse.rs b/library/test/src/formatters/terse.rs index 2931ca6ead0ac..22c28e4954eff 100644 --- a/library/test/src/formatters/terse.rs +++ b/library/test/src/formatters/terse.rs @@ -23,6 +23,7 @@ pub(crate) struct TerseFormatter { max_name_len: usize, test_count: usize, + test_column: usize, total_test_count: usize, } @@ -39,6 +40,7 @@ impl TerseFormatter { max_name_len, is_multithreaded, test_count: 0, + test_column: 0, total_test_count: 0, // initialized later, when write_run_start is called } } @@ -47,8 +49,20 @@ impl TerseFormatter { self.write_short_result(".", term::color::GREEN) } - pub fn write_failed(&mut self) -> io::Result<()> { - self.write_short_result("F", term::color::RED) + pub fn write_failed(&mut self, name: &str) -> io::Result<()> { + // Put failed tests on their own line and include the test name, so that it's faster + // to see which test failed without having to wait for them all to run. + + // normally, we write the progress unconditionally, even if the previous line was cut short. + // but if this is the very first column, no short results will have been printed and we'll end up with *only* the progress on the line. + // avoid this. + if self.test_column != 0 { + self.write_progress()?; + } + self.test_count += 1; + self.write_plain(format!("{name} --- "))?; + self.write_pretty("FAILED", term::color::RED)?; + self.write_plain("\n") } pub fn write_ignored(&mut self) -> io::Result<()> { @@ -65,15 +79,22 @@ impl TerseFormatter { color: term::color::Color, ) -> io::Result<()> { self.write_pretty(result, color)?; - if self.test_count % QUIET_MODE_MAX_COLUMN == QUIET_MODE_MAX_COLUMN - 1 { + self.test_count += 1; + self.test_column += 1; + if self.test_column % QUIET_MODE_MAX_COLUMN == QUIET_MODE_MAX_COLUMN - 1 { // We insert a new line regularly in order to flush the // screen when dealing with line-buffered output (e.g., piping to // `stamp` in the rust CI). - let out = format!(" {}/{}\n", self.test_count + 1, self.total_test_count); - self.write_plain(out)?; + self.write_progress()?; } - self.test_count += 1; + Ok(()) + } + + fn write_progress(&mut self) -> io::Result<()> { + let out = format!(" {}/{}\n", self.test_count, self.total_test_count); + self.write_plain(out)?; + self.test_column = 0; Ok(()) } @@ -213,7 +234,7 @@ impl OutputFormatter for TerseFormatter { match *result { TestResult::TrOk => self.write_ok(), TestResult::TrFailed | TestResult::TrFailedMsg(_) | TestResult::TrTimedFail => { - self.write_failed() + self.write_failed(desc.name.as_slice()) } TestResult::TrIgnored => self.write_ignored(), TestResult::TrBench(ref bs) => { diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index bfbb53f8c8121..cbd01606a895a 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -166,7 +166,7 @@ impl<'a> Renderer<'a> { println!(); } - fn render_test_outcome_terse(&mut self, outcome: Outcome<'_>, _: &TestOutcome) { + fn render_test_outcome_terse(&mut self, outcome: Outcome<'_>, test: &TestOutcome) { if self.terse_tests_in_line != 0 && self.terse_tests_in_line % TERSE_TESTS_PER_LINE == 0 { if let Some(total) = self.tests_count { let total = total.to_string(); @@ -178,7 +178,7 @@ impl<'a> Renderer<'a> { } self.terse_tests_in_line += 1; - self.builder.colored_stdout(|stdout| outcome.write_short(stdout)).unwrap(); + self.builder.colored_stdout(|stdout| outcome.write_short(stdout, &test.name)).unwrap(); let _ = std::io::stdout().flush(); } @@ -300,7 +300,7 @@ enum Outcome<'a> { } impl Outcome<'_> { - fn write_short(&self, writer: &mut dyn WriteColor) -> Result<(), std::io::Error> { + fn write_short(&self, writer: &mut dyn WriteColor, name: &str) -> Result<(), std::io::Error> { match self { Outcome::Ok => { writer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; @@ -311,8 +311,11 @@ impl Outcome<'_> { write!(writer, "b")?; } Outcome::Failed => { + // Put failed tests on their own line and include the test name, so that it's faster + // to see which test failed without having to wait for them all to run. + writeln!(writer)?; writer.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?; - write!(writer, "F")?; + writeln!(writer, "{name} ... F")?; } Outcome::Ignored { .. } => { writer.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?; diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index 5b76631833108..2f93252eddcd7 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -331,10 +331,29 @@ $ llvm-cov report \ ## `-C instrument-coverage=` -- `-C instrument-coverage=all`: Instrument all functions, including unused functions and unused generics. (This is the same as `-C instrument-coverage`, with no value.) -- `-C instrument-coverage=off`: Do not instrument any functions. (This is the same as simply not including the `-C instrument-coverage` option.) -- `-Zunstable-options -C instrument-coverage=except-unused-generics`: Instrument all functions except unused generics. -- `-Zunstable-options -C instrument-coverage=except-unused-functions`: Instrument only used (called) functions and instantiated generic functions. +- `-C instrument-coverage=no` (or `n`/`off`/`false`): + Don't enable coverage instrumentation. No functions will be instrumented for coverage. + - This is the same as not using the `-C instrument-coverage` flag at all. +- `-C instrument-coverage=yes` (or `y`/`on`/`true`): + Enable coverage instrumentation with the default behaviour. + Currently this instruments all functions, including unused functions and unused generics. + - This is the same as `-C instrument-coverage` with no value. + +### Other values + +- `-C instrument-coverage=all`: + Currently an alias for `yes`, but may behave differently in the future if + more fine-grained coverage options are added. + Using this value is currently not recommended. + +### Unstable values + +- `-Z unstable-options -C instrument-coverage=branch`: + Placeholder for potential branch coverage support in the future. +- `-Z unstable-options -C instrument-coverage=except-unused-generics`: + Instrument all functions except unused generics. +- `-Z unstable-options -C instrument-coverage=except-unused-functions`: + Instrument only used (called) functions and instantiated generic functions. ## Other references diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index be1c8d9094bfb..a1be60180838c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -1,5 +1,6 @@ //! Compute the binary representation of a type +use std::borrow::Cow; use std::fmt; use base_db::salsa::Cycle; @@ -114,8 +115,8 @@ struct LayoutCx<'a> { impl<'a> LayoutCalculator for LayoutCx<'a> { type TargetDataLayoutRef = &'a TargetDataLayout; - fn delayed_bug(&self, txt: String) { - never!("{}", txt); + fn delayed_bug(&self, txt: impl Into>) { + never!("{}", txt.into()); } fn current_data_layout(&self) -> &'a TargetDataLayout { diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 60a89a5753641..cb46e65999df6 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -360,6 +360,7 @@ mod tests { } fn build_diagnostic(level: DiagnosticLevel, span: Option) -> DiagInner { + #[allow(rustc::untranslatable_diagnostic)] // no translation needed for empty string let mut diag = DiagInner::new(level, ""); diag.messages.clear(); if let Some(span) = span { diff --git a/tests/ui-fulldeps/internal-lints/diagnostics.rs b/tests/ui-fulldeps/internal-lints/diagnostics.rs index 6a978dabe628f..f73fdc0882ff9 100644 --- a/tests/ui-fulldeps/internal-lints/diagnostics.rs +++ b/tests/ui-fulldeps/internal-lints/diagnostics.rs @@ -14,8 +14,8 @@ extern crate rustc_session; extern crate rustc_span; use rustc_errors::{ - AddToDiagnostic, Diag, EmissionGuarantee, DiagCtxt, IntoDiagnostic, Level, - SubdiagMessageOp, + AddToDiagnostic, DecorateLint, Diag, DiagCtxt, DiagInner, DiagMessage, EmissionGuarantee, + IntoDiagnostic, Level, SubdiagMessageOp, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::Span; @@ -78,6 +78,31 @@ impl AddToDiagnostic for TranslatableInAddToDiagnostic { } } +pub struct UntranslatableInDecorateLint; + +impl<'a> DecorateLint<'a, ()> for UntranslatableInDecorateLint { + fn decorate_lint<'b, >(self, diag: &'b mut Diag<'a, ()>) { + diag.note("untranslatable diagnostic"); + //~^ ERROR diagnostics should be created using translatable messages + } + + fn msg(&self) -> DiagMessage { + unreachable!(); + } +} + +pub struct TranslatableInDecorateLint; + +impl<'a> DecorateLint<'a, ()> for TranslatableInDecorateLint { + fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.note(crate::fluent_generated::no_crate_note); + } + + fn msg(&self) -> DiagMessage { + unreachable!(); + } +} + pub fn make_diagnostics<'a>(dcx: &'a DiagCtxt) { let _diag = dcx.struct_err(crate::fluent_generated::no_crate_example); //~^ ERROR diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls @@ -87,9 +112,11 @@ pub fn make_diagnostics<'a>(dcx: &'a DiagCtxt) { //~^^ ERROR diagnostics should be created using translatable messages } -// Check that `rustc_lint_diagnostics`-annotated functions aren't themselves linted. +// Check that `rustc_lint_diagnostics`-annotated functions aren't themselves linted for +// `diagnostic_outside_of_impl`. #[rustc_lint_diagnostics] pub fn skipped_because_of_annotation<'a>(dcx: &'a DiagCtxt) { + #[allow(rustc::untranslatable_diagnostic)] let _diag = dcx.struct_err("untranslatable diagnostic"); // okay! } diff --git a/tests/ui-fulldeps/internal-lints/diagnostics.stderr b/tests/ui-fulldeps/internal-lints/diagnostics.stderr index 5ddfdf32149aa..b8fbee1ff009c 100644 --- a/tests/ui-fulldeps/internal-lints/diagnostics.stderr +++ b/tests/ui-fulldeps/internal-lints/diagnostics.stderr @@ -16,8 +16,14 @@ error: diagnostics should be created using translatable messages LL | diag.note("untranslatable diagnostic"); | ^^^^ +error: diagnostics should be created using translatable messages + --> $DIR/diagnostics.rs:85:14 + | +LL | diag.note("untranslatable diagnostic"); + | ^^^^ + error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls - --> $DIR/diagnostics.rs:82:21 + --> $DIR/diagnostics.rs:107:21 | LL | let _diag = dcx.struct_err(crate::fluent_generated::no_crate_example); | ^^^^^^^^^^ @@ -29,16 +35,16 @@ LL | #![deny(rustc::diagnostic_outside_of_impl)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls - --> $DIR/diagnostics.rs:85:21 + --> $DIR/diagnostics.rs:110:21 | LL | let _diag = dcx.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:85:21 + --> $DIR/diagnostics.rs:110:21 | LL | let _diag = dcx.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/imports/append-import-suggestion.rs b/tests/ui/imports/append-import-suggestion.rs new file mode 100644 index 0000000000000..6b75804b86fcb --- /dev/null +++ b/tests/ui/imports/append-import-suggestion.rs @@ -0,0 +1,16 @@ +// https://github.com/rust-lang/rust/issues/114884 + +mod mod1 { + pub trait TraitA {} +} + +mod mod2 { + mod sub_mod { + use super::super::mod1::TraitA; + } +} + +use mod2::{sub_mod::TraitA}; +//~^ ERROR: module `sub_mod` is private + +fn main() {} diff --git a/tests/ui/imports/append-import-suggestion.stderr b/tests/ui/imports/append-import-suggestion.stderr new file mode 100644 index 0000000000000..6d7b657f3d128 --- /dev/null +++ b/tests/ui/imports/append-import-suggestion.stderr @@ -0,0 +1,21 @@ +error[E0603]: module `sub_mod` is private + --> $DIR/append-import-suggestion.rs:13:12 + | +LL | use mod2::{sub_mod::TraitA}; + | ^^^^^^^ private module + | +help: consider importing this trait instead: + mod1::TraitA + --> $DIR/append-import-suggestion.rs:13:12 + | +LL | use mod2::{sub_mod::TraitA}; + | ^^^^^^^^^^^^^^^ +note: the module `sub_mod` is defined here + --> $DIR/append-import-suggestion.rs:8:5 + | +LL | mod sub_mod { + | ^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/include-macros/parent_dir.rs b/tests/ui/include-macros/parent_dir.rs new file mode 100644 index 0000000000000..4933ec4373c61 --- /dev/null +++ b/tests/ui/include-macros/parent_dir.rs @@ -0,0 +1,10 @@ +fn main() { + let _ = include_str!("include-macros/file.txt"); //~ ERROR couldn't read + //~^HELP different directory + let _ = include_str!("hello.rs"); //~ ERROR couldn't read + //~^HELP different directory + let _ = include_bytes!("../../data.bin"); //~ ERROR couldn't read + //~^HELP different directory + let _ = include_str!("tests/ui/include-macros/file.txt"); //~ ERROR couldn't read + //~^HELP different directory +} diff --git a/tests/ui/include-macros/parent_dir.stderr b/tests/ui/include-macros/parent_dir.stderr new file mode 100644 index 0000000000000..3ec4c34763e4f --- /dev/null +++ b/tests/ui/include-macros/parent_dir.stderr @@ -0,0 +1,50 @@ +error: couldn't read `$DIR/include-macros/file.txt`: No such file or directory (os error 2) + --> $DIR/parent_dir.rs:2:13 + | +LL | let _ = include_str!("include-macros/file.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) +help: there is a file with the same name in a different directory + | +LL | let _ = include_str!("file.txt"); + | ~~~~~~~~~~ + +error: couldn't read `$DIR/hello.rs`: No such file or directory (os error 2) + --> $DIR/parent_dir.rs:4:13 + | +LL | let _ = include_str!("hello.rs"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) +help: there is a file with the same name in a different directory + | +LL | let _ = include_str!("../hello.rs"); + | ~~~~~~~~~~~~~ + +error: couldn't read `$DIR/../../data.bin`: No such file or directory (os error 2) + --> $DIR/parent_dir.rs:6:13 + | +LL | let _ = include_bytes!("../../data.bin"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) +help: there is a file with the same name in a different directory + | +LL | let _ = include_bytes!("data.bin"); + | ~~~~~~~~~~ + +error: couldn't read `$DIR/tests/ui/include-macros/file.txt`: No such file or directory (os error 2) + --> $DIR/parent_dir.rs:8:13 + | +LL | let _ = include_str!("tests/ui/include-macros/file.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) +help: there is a file with the same name in a different directory + | +LL | let _ = include_str!("file.txt"); + | ~~~~~~~~~~ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/instrument-coverage/bad-value.bad.stderr b/tests/ui/instrument-coverage/bad-value.bad.stderr index b867d169dae5b..0411a1f98a3ee 100644 --- a/tests/ui/instrument-coverage/bad-value.bad.stderr +++ b/tests/ui/instrument-coverage/bad-value.bad.stderr @@ -1,2 +1,2 @@ -error: incorrect value `bad-value` for codegen option `instrument-coverage` - `all` (default), `branch`, `except-unused-generics`, `except-unused-functions`, or `off` was expected +error: incorrect value `bad-value` for codegen option `instrument-coverage` - either a boolean (`yes`, `no`, `on`, `off`, etc) or (unstable) one of `branch`, `except-unused-generics`, `except-unused-functions` was expected diff --git a/tests/ui/instrument-coverage/bad-value.blank.stderr b/tests/ui/instrument-coverage/bad-value.blank.stderr index e7122fb61cdfc..b3a8e7cf94784 100644 --- a/tests/ui/instrument-coverage/bad-value.blank.stderr +++ b/tests/ui/instrument-coverage/bad-value.blank.stderr @@ -1,2 +1,2 @@ -error: incorrect value `` for codegen option `instrument-coverage` - `all` (default), `branch`, `except-unused-generics`, `except-unused-functions`, or `off` was expected +error: incorrect value `` for codegen option `instrument-coverage` - either a boolean (`yes`, `no`, `on`, `off`, etc) or (unstable) one of `branch`, `except-unused-generics`, `except-unused-functions` was expected diff --git a/tests/ui/lint/lint-qualification.fixed b/tests/ui/lint/lint-qualification.fixed index ab450f8b7347d..2b1f8b591752a 100644 --- a/tests/ui/lint/lint-qualification.fixed +++ b/tests/ui/lint/lint-qualification.fixed @@ -24,6 +24,9 @@ fn main() { use std::fmt; let _: fmt::Result = Ok(()); //~ ERROR: unnecessary qualification + let _ = ::default(); // issue #121999 + //~^ ERROR: unnecessary qualification + macro_rules! m { ($a:ident, $b:ident) => { $crate::foo::bar(); // issue #37357 ::foo::bar(); // issue #38682 diff --git a/tests/ui/lint/lint-qualification.rs b/tests/ui/lint/lint-qualification.rs index 84a36f509eb9e..002fdbf7724a7 100644 --- a/tests/ui/lint/lint-qualification.rs +++ b/tests/ui/lint/lint-qualification.rs @@ -24,6 +24,9 @@ fn main() { use std::fmt; let _: std::fmt::Result = Ok(()); //~ ERROR: unnecessary qualification + let _ = ::default(); // issue #121999 + //~^ ERROR: unnecessary qualification + macro_rules! m { ($a:ident, $b:ident) => { $crate::foo::bar(); // issue #37357 ::foo::bar(); // issue #38682 diff --git a/tests/ui/lint/lint-qualification.stderr b/tests/ui/lint/lint-qualification.stderr index 45e1525f4bf8c..8dddcf23f7575 100644 --- a/tests/ui/lint/lint-qualification.stderr +++ b/tests/ui/lint/lint-qualification.stderr @@ -87,5 +87,17 @@ LL - let _: std::fmt::Result = Ok(()); LL + let _: fmt::Result = Ok(()); | -error: aborting due to 7 previous errors +error: unnecessary qualification + --> $DIR/lint-qualification.rs:27:13 + | +LL | let _ = ::default(); // issue #121999 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the unnecessary path segments + | +LL - let _ = ::default(); // issue #121999 +LL + let _ = ::default(); // issue #121999 + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/macros/macros-nonfatal-errors.stderr b/tests/ui/macros/macros-nonfatal-errors.stderr index ca373ea6cd9e4..2b3cff9891e1e 100644 --- a/tests/ui/macros/macros-nonfatal-errors.stderr +++ b/tests/ui/macros/macros-nonfatal-errors.stderr @@ -200,7 +200,7 @@ error: argument must be a string literal LL | include_str!(invalid); | ^^^^^^^ -error: couldn't read $DIR/i'd be quite surprised if a file with this name existed: $FILE_NOT_FOUND_MSG (os error 2) +error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: No such file or directory (os error 2) --> $DIR/macros-nonfatal-errors.rs:113:5 | LL | include_str!("i'd be quite surprised if a file with this name existed"); @@ -214,7 +214,7 @@ error: argument must be a string literal LL | include_bytes!(invalid); | ^^^^^^^ -error: couldn't read $DIR/i'd be quite surprised if a file with this name existed: $FILE_NOT_FOUND_MSG (os error 2) +error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: No such file or directory (os error 2) --> $DIR/macros-nonfatal-errors.rs:115:5 | LL | include_bytes!("i'd be quite surprised if a file with this name existed"); diff --git a/tests/ui/test-attrs/terse.rs b/tests/ui/test-attrs/terse.rs new file mode 100644 index 0000000000000..ab9d5cc19bd6c --- /dev/null +++ b/tests/ui/test-attrs/terse.rs @@ -0,0 +1,125 @@ +//@ compile-flags: --test +//@ run-fail +//@ run-flags: --test-threads=1 --quiet +//@ check-run-results +//@ exec-env:RUST_BACKTRACE=0 +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ ignore-emscripten no threads support +//@ needs-unwind + +#[test] +fn abc() { + panic!(); +} + +#[test] +fn foo() { + panic!(); +} + +#[test] +fn foo2() { + panic!(); +} + +// run a whole bunch of tests so we can see what happens when we go over 88 columns +#[test] fn f0() {} +#[test] fn f1() {} +#[test] fn f2() {} +#[test] fn f3() {} +#[test] fn f4() {} +#[test] fn f5() {} +#[test] fn f6() {} +#[test] fn f7() {} +#[test] fn f8() {} +#[test] fn f9() {} +#[test] fn f10() {} +#[test] fn f11() {} +#[test] fn f12() {} +#[test] fn f13() {} +#[test] fn f14() {} +#[test] fn f15() {} +#[test] fn f16() {} +#[test] fn f17() {} +#[test] fn f18() {} +#[test] fn f19() {} +#[test] fn f20() {} +#[test] fn f21() {} +#[test] fn f22() {} +#[test] fn f23() {} +#[test] fn f24() {} +#[test] fn f25() {} +#[test] fn f26() {} +#[test] fn f27() {} +#[test] fn f28() {} +#[test] fn f29() {} +#[test] fn f30() {} +#[test] fn f31() {} +#[test] fn f32() {} +#[test] fn f33() {} +#[test] fn f34() {} +#[test] fn f35() {} +#[test] fn f36() {} +#[test] fn f37() {} +#[test] fn f38() {} +#[test] fn f39() {} +#[test] fn f40() {} +#[test] fn f41() {} +#[test] fn f42() {} +#[test] fn f43() {} +#[test] fn f44() {} +#[test] fn f45() {} +#[test] fn f46() {} +#[test] fn f47() {} +#[test] fn f48() {} +#[test] fn f49() {} +#[test] fn f50() {} +#[test] fn f51() {} +#[test] fn f52() {} +#[test] fn f53() {} +#[test] fn f54() {} +#[test] fn f55() {} +#[test] fn f56() {} +#[test] fn f57() {} +#[test] fn f58() {} +#[test] fn f59() {} +#[test] fn f60() {} +#[test] fn f61() {} +#[test] fn f62() {} +#[test] fn f63() {} +#[test] fn f64() {} +#[test] fn f65() {} +#[test] fn f66() {} +#[test] fn f67() {} +#[test] fn f68() {} +#[test] fn f69() {} +#[test] fn f70() {} +#[test] fn f71() {} +#[test] fn f72() {} +#[test] fn f73() {} +#[test] fn f74() {} +#[test] fn f75() {} +#[test] fn f76() {} +#[test] fn f77() {} +#[test] fn f78() {} +#[test] fn f79() {} +#[test] fn f80() {} +#[test] fn f81() {} +#[test] fn f82() {} +#[test] fn f83() {} +#[test] fn f84() {} +#[test] fn f85() {} +#[test] fn f86() {} +#[test] fn f87() {} +#[test] fn f88() {} +#[test] fn f89() {} +#[test] fn f90() {} +#[test] fn f91() {} +#[test] fn f92() {} +#[test] fn f93() {} +#[test] fn f94() {} +#[test] fn f95() {} +#[test] fn f96() {} +#[test] fn f97() {} +#[test] fn f98() {} +#[test] fn f99() {} diff --git a/tests/ui/test-attrs/terse.run.stdout b/tests/ui/test-attrs/terse.run.stdout new file mode 100644 index 0000000000000..2b361361ae888 --- /dev/null +++ b/tests/ui/test-attrs/terse.run.stdout @@ -0,0 +1,31 @@ + +running 103 tests +abc --- FAILED +....................................................................................... 88/103 +............. 101/103 +foo --- FAILED +foo2 --- FAILED + +failures: + +---- abc stdout ---- +thread 'abc' panicked at $DIR/terse.rs:12:5: +explicit panic +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + +---- foo stdout ---- +thread 'foo' panicked at $DIR/terse.rs:17:5: +explicit panic + +---- foo2 stdout ---- +thread 'foo2' panicked at $DIR/terse.rs:22:5: +explicit panic + + +failures: + abc + foo + foo2 + +test result: FAILED. 100 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +