diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index ee46bdcf7d8bb..fda239560a89e 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1681,19 +1681,28 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_item_safety(span, safety); } - if let FnKind::Fn(ctxt, _, fun) = fk - && let Extern::Explicit(str_lit, extern_abi_span) = fun.sig.header.ext - && let Ok(abi) = ExternAbi::from_str(str_lit.symbol.as_str()) - { - self.check_extern_fn_signature(abi, ctxt, &fun.ident, &fun.sig); + if let FnKind::Fn(ctxt, _, fun) = fk { + let ext = match fun.sig.header.ext { + Extern::None => None, + Extern::Implicit(span) => Some((ExternAbi::FALLBACK, span)), + Extern::Explicit(str_lit, span) => { + ExternAbi::from_str(str_lit.symbol.as_str()).ok().map(|abi| (abi, span)) + } + }; - if let Some(attr) = attr::find_by_name(attrs, sym::track_caller) - && abi != ExternAbi::Rust - { - self.dcx().emit_err(errors::RequiresRustAbi { - track_caller_span: attr.span, - extern_abi_span, - }); + if let Some((extern_abi, extern_abi_span)) = ext { + // Some ABIs impose special restrictions on the signature. + self.check_extern_fn_signature(extern_abi, ctxt, &fun.ident, &fun.sig); + + // #[track_caller] can only be used with the rust ABI. + if let Some(attr) = attr::find_by_name(attrs, sym::track_caller) + && extern_abi != ExternAbi::Rust + { + self.dcx().emit_err(errors::RequiresRustAbi { + track_caller_span: attr.span, + extern_abi_span, + }); + } } } diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index e9d868039380b..06738b9c73375 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -64,6 +64,7 @@ impl<'sess> AttributeParser<'sess, Early> { sym, target_span, target_node_id, + Target::Crate, // Does not matter, we're not going to emit errors anyways features, ShouldEmit::Nothing, ) @@ -79,6 +80,7 @@ impl<'sess> AttributeParser<'sess, Early> { sym: Symbol, target_span: Span, target_node_id: NodeId, + target: Target, features: Option<&'sess Features>, should_emit: ShouldEmit, ) -> Option { @@ -86,7 +88,7 @@ impl<'sess> AttributeParser<'sess, Early> { sess, attrs, Some(sym), - Target::Crate, // Does not matter, we're not going to emit errors anyways + target, target_span, target_node_id, features, diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index cddb37c7d816f..2816f5ddc6460 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -6,11 +6,10 @@ use std::ops::ControlFlow; use either::Either; use hir::{ClosureKind, Path}; use rustc_data_structures::fx::FxIndexSet; -use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, MultiSpan, struct_span_code_err}; use rustc_hir as hir; -use rustc_hir::attrs::diagnostic::FormatArgs; +use rustc_hir::attrs::diagnostic::{CustomDiagnostic, FormatArgs}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr}; use rustc_hir::{ @@ -146,7 +145,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.body.local_decls[moved_place.local].ty.kind() && let Some(Some(directive)) = find_attr!(self.infcx.tcx, item_def.did(), OnMove { directive, .. } => directive) { - let item_name = self.infcx.tcx.item_name(item_def.did()).to_string(); + let this = self.infcx.tcx.item_name(item_def.did()).to_string(); let mut generic_args: Vec<_> = self .infcx .tcx @@ -155,21 +154,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .iter() .filter_map(|param| Some((param.name, args[param.index as usize].to_string()))) .collect(); - generic_args.push((kw::SelfUpper, item_name)); + generic_args.push((kw::SelfUpper, this.clone())); let args = FormatArgs { - this: String::new(), - trait_sugared: String::new(), + this, + // Unused + this_sugared: String::new(), + // Unused item_context: "", generic_args, }; - ( - directive.message.as_ref().map(|e| e.1.format(&args)), - directive.label.as_ref().map(|e| e.1.format(&args)), - directive.notes.iter().map(|e| e.format(&args)).collect(), - ) + let CustomDiagnostic { message, label, notes, parent_label: _ } = + directive.eval(None, &args); + + (message, label, notes) } else { - (None, None, ThinVec::new()) + (None, None, Vec::new()) }; let mut err = self.cannot_act_on_moved_value( diff --git a/compiler/rustc_hir/src/attrs/diagnostic.rs b/compiler/rustc_hir/src/attrs/diagnostic.rs index c700ca142759e..655a3b3fb05de 100644 --- a/compiler/rustc_hir/src/attrs/diagnostic.rs +++ b/compiler/rustc_hir/src/attrs/diagnostic.rs @@ -52,30 +52,40 @@ impl Directive { } } - pub fn evaluate_directive( + pub fn eval( &self, - trait_name: impl Debug, - condition_options: &ConditionOptions, + condition_options: Option<&ConditionOptions>, args: &FormatArgs, - ) -> OnUnimplementedNote { + ) -> CustomDiagnostic { + let this = &args.this; + info!("eval({self:?}, this={this}, options={condition_options:?}, args ={args:?})"); + + let Some(condition_options) = condition_options else { + debug_assert!( + !self.is_rustc_attr, + "Directive::eval called for `rustc_on_unimplemented` without `condition_options`" + ); + return CustomDiagnostic { + label: self.label.as_ref().map(|l| l.1.format(args)), + message: self.message.as_ref().map(|m| m.1.format(args)), + notes: self.notes.iter().map(|n| n.format(args)).collect(), + parent_label: None, + }; + }; let mut message = None; let mut label = None; let mut notes = Vec::new(); let mut parent_label = None; - info!( - "evaluate_directive({:?}, trait_ref={:?}, options={:?}, args ={:?})", - self, trait_name, condition_options, args - ); for command in self.subcommands.iter().chain(Some(self)).rev() { debug!(?command); if let Some(ref condition) = command.condition && !condition.matches_predicate(condition_options) { - debug!("evaluate_directive: skipping {:?} due to condition", command); + debug!("eval: skipping {command:?} due to condition"); continue; } - debug!("evaluate_directive: {:?} succeeded", command); + debug!("eval: {command:?} succeeded"); if let Some(ref message_) = command.message { message = Some(message_.clone()); } @@ -91,7 +101,7 @@ impl Directive { } } - OnUnimplementedNote { + CustomDiagnostic { label: label.map(|l| l.1.format(args)), message: message.map(|m| m.1.format(args)), notes: notes.into_iter().map(|n| n.format(args)).collect(), @@ -100,8 +110,9 @@ impl Directive { } } +/// A custom diagnostic, created from a diagnostic attribute. #[derive(Default, Debug)] -pub struct OnUnimplementedNote { +pub struct CustomDiagnostic { pub message: Option, pub label: Option, pub notes: Vec, @@ -116,8 +127,13 @@ pub struct FormatString { pub span: Span, pub pieces: ThinVec, } + impl FormatString { - pub fn format(&self, args: &FormatArgs) -> String { + /// Formats the format string. + /// + /// This is a private method, use `Directive::eval` instead. A diagnostic attribute being used + /// should issue a `tracing` event, which `Directive::eval` does. + fn format(&self, args: &FormatArgs) -> String { let mut ret = String::new(); for piece in &self.pieces { match piece { @@ -147,7 +163,7 @@ impl FormatString { // It's only `rustc_onunimplemented` from here Piece::Arg(FormatArg::This) => ret.push_str(&args.this), Piece::Arg(FormatArg::Trait) => { - let _ = fmt::write(&mut ret, format_args!("{}", &args.trait_sugared)); + let _ = fmt::write(&mut ret, format_args!("{}", &args.this_sugared)); } Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context), } @@ -193,7 +209,7 @@ impl FormatString { /// ```rust,ignore (just an example) /// FormatArgs { /// this: "FromResidual", -/// trait_sugared: "FromResidual>", +/// this_sugared: "FromResidual>", /// item_context: "an async function", /// generic_args: [("Self", "u32"), ("R", "Option")], /// } @@ -201,7 +217,7 @@ impl FormatString { #[derive(Debug)] pub struct FormatArgs { pub this: String, - pub trait_sugared: String, + pub this_sugared: String, pub item_context: &'static str, pub generic_args: Vec<(Symbol, String)>, } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 85bcc2745254d..b46a09bf42e96 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -16,7 +16,7 @@ use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, MultiSpan, StashKey, StringPart, listify, pluralize, struct_span_code_err, }; -use rustc_hir::attrs::diagnostic::OnUnimplementedNote; +use rustc_hir::attrs::diagnostic::CustomDiagnostic; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; @@ -2067,7 +2067,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Avoid crashing. return (None, None, Vec::new()); } - let OnUnimplementedNote { message, label, notes, .. } = self + let CustomDiagnostic { message, label, notes, .. } = self .err_ctxt() .on_unimplemented_note(trait_ref, &obligation, err.long_ty_path()); (message, label, notes) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 7ba7fd58081a8..43efce545fc28 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -22,7 +22,7 @@ use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap}; use rustc_hir::definitions::Definitions; use rustc_hir::limit::Limit; use rustc_hir::lints::DelayedLint; -use rustc_hir::{Attribute, MaybeOwner, find_attr}; +use rustc_hir::{Attribute, MaybeOwner, Target, find_attr}; use rustc_incremental::setup_dep_graph; use rustc_lint::{ BufferedEarlyLint, DecorateAttrLint, EarlyCheckNode, LintStore, unerased_lint_store, @@ -1372,6 +1372,7 @@ pub(crate) fn parse_crate_name( sym::crate_name, DUMMY_SP, rustc_ast::node_id::CRATE_NODE_ID, + Target::Crate, None, emit_errors, )? @@ -1421,6 +1422,7 @@ pub fn collect_crate_types( sym::crate_type, crate_span, CRATE_NODE_ID, + Target::Crate, None, ShouldEmit::EarlyFatal { also_emit_lints: false }, ) @@ -1477,6 +1479,7 @@ fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit sym::recursion_limit, DUMMY_SP, rustc_ast::node_id::CRATE_NODE_ID, + Target::Crate, None, // errors are fatal here, but lints aren't. // If things aren't fatal we continue, and will parse this again. diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 79007f64a632e..3d7db3ed25b0a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -14,7 +14,7 @@ use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, Level, MultiSpan, StashKey, StringPart, Suggestions, msg, pluralize, struct_span_code_err, }; -use rustc_hir::attrs::diagnostic::OnUnimplementedNote; +use rustc_hir::attrs::diagnostic::CustomDiagnostic; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, LangItem, Node, find_attr}; @@ -188,7 +188,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) .unwrap_or_default(); - let OnUnimplementedNote { + let CustomDiagnostic { message, label, notes, @@ -907,12 +907,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); if let Some(command) = find_attr!(self.tcx, impl_did, OnConst {directive, ..} => directive.as_deref()).flatten(){ - let note = command.evaluate_directive( - predicate.skip_binder().trait_ref, - &condition_options, + let note = command.eval( + Some(&condition_options), &format_args, ); - let OnUnimplementedNote { + let CustomDiagnostic { message, label, notes, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 4ae6584fc2d1f..ba7c8d8f1dac1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use rustc_hir as hir; -use rustc_hir::attrs::diagnostic::{ConditionOptions, FormatArgs, OnUnimplementedNote}; +use rustc_hir::attrs::diagnostic::{ConditionOptions, CustomDiagnostic, FormatArgs}; use rustc_hir::def_id::LocalDefId; use rustc_hir::find_attr; pub use rustc_hir::lints::FormatWarning; @@ -37,20 +37,19 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, obligation: &PredicateObligation<'tcx>, long_ty_path: &mut Option, - ) -> OnUnimplementedNote { + ) -> CustomDiagnostic { if trait_pred.polarity() != ty::PredicatePolarity::Positive { - return OnUnimplementedNote::default(); + return CustomDiagnostic::default(); } let (condition_options, format_args) = self.on_unimplemented_components(trait_pred, obligation, long_ty_path); if let Some(command) = find_attr!(self.tcx, trait_pred.def_id(), OnUnimplemented {directive, ..} => directive.as_deref()).flatten() { - command.evaluate_directive( - trait_pred.skip_binder().trait_ref, - &condition_options, + command.eval( + Some(&condition_options), &format_args, ) } else { - OnUnimplementedNote::default() + CustomDiagnostic::default() } } @@ -211,7 +210,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { })); let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id); - let trait_sugared = trait_pred.trait_ref.print_trait_sugared().to_string(); + let this_sugared = trait_pred.trait_ref.print_trait_sugared().to_string(); let condition_options = ConditionOptions { self_types, @@ -249,7 +248,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }) .collect(); - let format_args = FormatArgs { this, trait_sugared, generic_args, item_context }; + let format_args = FormatArgs { this, this_sugared, generic_args, item_context }; (condition_options, format_args) } } diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.rs index 53f99760d88f3..a8dfa514d0700 100644 --- a/tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.rs +++ b/tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.rs @@ -27,4 +27,10 @@ impl S { extern "Rust" fn rust_method() {} } +#[rustfmt::skip] // rustfmt will insert the implicit "C" +#[track_caller] +//~^ ERROR `#[track_caller]` can only be used with the Rust ABI +pub extern fn extern_missing_abi() {} +//~^ WARN `extern` declarations without an explicit ABI are deprecated + fn main() {} diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.stderr index f5cba7b8a011c..2a1fac08b103a 100644 --- a/tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.stderr +++ b/tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.stderr @@ -24,6 +24,23 @@ LL | LL | extern "C" fn c_method() {} | ---------- not using the Rust ABI because of this -error: aborting due to 3 previous errors +error[E0737]: `#[track_caller]` can only be used with the Rust ABI + --> $DIR/error-with-invalid-abi.rs:31:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ using `#[track_caller]` here +LL | +LL | pub extern fn extern_missing_abi() {} + | ------ not using the Rust ABI because of this + +warning: `extern` declarations without an explicit ABI are deprecated + --> $DIR/error-with-invalid-abi.rs:33:5 + | +LL | pub extern fn extern_missing_abi() {} + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + | + = note: `#[warn(missing_abi)]` on by default + +error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0737`.