use rustc_hir::{MethodKind, Target, Unsafety};
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault;
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{ParamEnv, TyCtxt};
use rustc_session::lint::builtin::{
struct CheckAttrVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
+
+ // Whether or not this visitor should abort after finding errors
abort: Cell<bool>,
}
let attrs = self.tcx.hir().attrs(hir_id);
for attr in attrs {
let attr_is_valid = match attr.name_or_empty() {
+ sym::do_not_recommend => self.check_do_not_recommend(attr.span, target),
sym::inline => self.check_inline(hir_id, attr, span, target),
sym::no_coverage => self.check_no_coverage(hir_id, attr, span, target),
sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
);
}
+ /// Checks if `#[do_not_recommend]` is applied on a trait impl.
+ fn check_do_not_recommend(&self, attr_span: Span, target: Target) -> bool {
+ if let Target::Impl = target {
+ true
+ } else {
+ self.tcx.sess.emit_err(errors::IncorrectDoNotRecommendLocation { span: attr_span });
+ false
+ }
+ }
+
/// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
match target {
);
}
+ /// A best effort attempt to create an error for a mismatching proc macro signature.
+ ///
+ /// If this best effort goes wrong, it will just emit a worse error later (see #102923)
fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) {
let expected_input_count = match kind {
ProcMacroKind::Attribute => 2,
let id = hir_id.expect_owner();
let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id).unwrap();
- let sig = tcx.fn_sig(id);
+ let sig = tcx.liberate_late_bound_regions(id.to_def_id(), tcx.fn_sig(id));
+ let sig = tcx.normalize_erasing_regions(ParamEnv::empty(), sig);
+
+ // We don't currently require that the function signature is equal to
+ // `fn(TokenStream) -> TokenStream`, but instead monomorphizes to
+ // `fn(TokenStream) -> TokenStream` after some substitution of generic arguments.
+ //
+ // Properly checking this means pulling in additional `rustc` crates, so we don't.
+ let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer };
- if sig.abi() != Abi::Rust {
- tcx.sess
- .emit_err(ProcMacroInvalidAbi { span: hir_sig.span, abi: sig.abi().name() });
+ if sig.abi != Abi::Rust {
+ tcx.sess.emit_err(ProcMacroInvalidAbi { span: hir_sig.span, abi: sig.abi.name() });
self.abort.set(true);
}
- if sig.unsafety() == Unsafety::Unsafe {
+ if sig.unsafety == Unsafety::Unsafe {
tcx.sess.emit_err(ProcMacroUnsafe { span: hir_sig.span });
self.abort.set(true);
}
- let output = sig.output().skip_binder();
+ let output = sig.output();
// Typecheck the output
- if tcx.normalize_erasing_regions(ParamEnv::empty(), output) != tokenstream {
+ if !drcx.types_may_unify(output, tokenstream) {
tcx.sess.emit_err(ProcMacroTypeError {
span: hir_sig.decl.output.span(),
found: output,
self.abort.set(true);
}
- // Typecheck "expected_input_count" inputs, emitting
- // `ProcMacroMissingArguments` if there are not enough.
- if let Some(args) = sig.inputs().skip_binder().get(0..expected_input_count) {
- for (arg, input) in args.iter().zip(hir_sig.decl.inputs) {
- if tcx.normalize_erasing_regions(ParamEnv::empty(), *arg) != tokenstream {
+ if sig.inputs().len() < expected_input_count {
+ tcx.sess.emit_err(ProcMacroMissingArguments {
+ expected_input_count,
+ span: hir_sig.span,
+ kind,
+ expected_signature,
+ });
+ self.abort.set(true);
+ }
+
+ // Check that the inputs are correct, if there are enough.
+ if sig.inputs().len() >= expected_input_count {
+ for (arg, input) in
+ sig.inputs().iter().zip(hir_sig.decl.inputs).take(expected_input_count)
+ {
+ if !drcx.types_may_unify(*arg, tokenstream) {
tcx.sess.emit_err(ProcMacroTypeError {
span: input.span,
found: *arg,
self.abort.set(true);
}
}
- } else {
- tcx.sess.emit_err(ProcMacroMissingArguments {
- expected_input_count,
- span: hir_sig.span,
- kind,
- expected_signature,
- });
- self.abort.set(true);
}
// Check that there are not too many arguments