X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=compiler%2Frustc_passes%2Fsrc%2Fcheck_attr.rs;h=42329853259459ead163c2e4e7c70fc28084be19;hb=f3644ca64de6357a07a3901dd2dacbda247beec6;hp=517bf2533c5e604becdddd503a2ce8b4e25c56da;hpb=f92000816e0cf8bdfe6ad422464ce0fa6144b496;p=rust.git diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 517bf2533c5..42329853259 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -23,6 +23,7 @@ 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::{ @@ -84,6 +85,8 @@ fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { struct CheckAttrVisitor<'tcx> { tcx: TyCtxt<'tcx>, + + // Whether or not this visitor should abort after finding errors abort: Cell, } @@ -103,6 +106,7 @@ fn check_attributes( 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), @@ -272,6 +276,16 @@ fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribut ); } + /// 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 { @@ -2084,6 +2098,9 @@ fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) { ); } + /// 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, @@ -2103,23 +2120,30 @@ fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) { 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, @@ -2129,11 +2153,22 @@ fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) { 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, @@ -2143,14 +2178,6 @@ fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) { 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