Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_attr_parsing/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand All @@ -79,14 +80,15 @@ impl<'sess> AttributeParser<'sess, Early> {
sym: Symbol,
target_span: Span,
target_node_id: NodeId,
target: Target,
features: Option<&'sess Features>,
should_emit: ShouldEmit,
) -> Option<Attribute> {
let mut parsed = Self::parse_limited_all(
sess,
attrs,
Some(sym),
Target::Crate, // Does not matter, we're not going to emit errors anyways
target,
target_span,
target_node_id,
features,
Expand Down
24 changes: 12 additions & 12 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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
Expand All @@ -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(
Expand Down
48 changes: 32 additions & 16 deletions compiler/rustc_hir/src/attrs/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand All @@ -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(),
Expand All @@ -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<String>,
pub label: Option<String>,
pub notes: Vec<String>,
Expand All @@ -116,8 +127,13 @@ pub struct FormatString {
pub span: Span,
pub pieces: ThinVec<Piece>,
}

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 {
Expand Down Expand Up @@ -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),
}
Expand Down Expand Up @@ -193,15 +209,15 @@ impl FormatString {
/// ```rust,ignore (just an example)
/// FormatArgs {
/// this: "FromResidual",
/// trait_sugared: "FromResidual<Option<Infallible>>",
/// this_sugared: "FromResidual<Option<Infallible>>",
/// item_context: "an async function",
/// generic_args: [("Self", "u32"), ("R", "Option<Infallible>")],
/// }
/// ```
#[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)>,
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
)?
Expand Down Expand Up @@ -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 },
)
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -188,7 +188,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
})
.unwrap_or_default();

let OnUnimplementedNote {
let CustomDiagnostic {
message,
label,
notes,
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -37,20 +37,19 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
obligation: &PredicateObligation<'tcx>,
long_ty_path: &mut Option<PathBuf>,
) -> 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()
}
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
}
}
6 changes: 6 additions & 0 deletions tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
Loading
Loading