use rustc_session::parse::feature_err;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
+use rustc_target::spec::abi::Abi;
use std::collections::hash_map::Entry;
pub(crate) fn target_from_impl_item<'tcx>(
for attr in attrs {
let attr_is_valid = match attr.name_or_empty() {
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),
sym::marker => self.check_marker(hir_id, attr, span, target),
sym::rustc_must_implement_one_of => {
}
}
+ /// Checks if a `#[no_coverage]` is applied directly to a function
+ fn check_no_coverage(
+ &self,
+ hir_id: HirId,
+ attr: &Attribute,
+ span: Span,
+ target: Target,
+ ) -> bool {
+ match target {
+ // no_coverage on function is fine
+ Target::Fn
+ | Target::Closure
+ | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
+
+ // function prototypes can't be covered
+ Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
+ self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+ lint.build("`#[no_coverage]` is ignored on function prototypes").emit();
+ });
+ true
+ }
+
+ Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => {
+ self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+ lint.build("`#[no_coverage]` does not propagate into items and must be applied to the contained functions directly").emit();
+ });
+ true
+ }
+
+ Target::Expression | Target::Statement | Target::Arm => {
+ self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+ lint.build("`#[no_coverage]` may only be applied to function definitions")
+ .emit();
+ });
+ true
+ }
+
+ _ => {
+ struct_span_err!(
+ self.tcx.sess,
+ attr.span,
+ E0788,
+ "`#[no_coverage]` must be applied to coverable code",
+ )
+ .span_label(span, "not coverable code")
+ .emit();
+ false
+ }
+ }
+ }
+
fn check_generic_attr(
&self,
hir_id: HirId,
let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
let containing_item = self.tcx.hir().expect_item(parent_hir_id);
- if Target::from_item(containing_item) == Target::Impl {
+ if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = containing_item.kind {
let meta_items = attr.meta_item_list().unwrap();
let (span, replacement_span) = if meta_items.len() == 1 {
/// Checks if `#[link]` is applied to an item other than a foreign module.
fn check_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
- match target {
- Target::ForeignMod => {}
- _ => {
- self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
- let mut diag = lint.build("attribute should be applied to an `extern` block");
- diag.warn(
- "this was previously accepted by the compiler but is \
- being phased out; it will become a hard error in \
- a future release!",
- );
+ if target == Target::ForeignMod
+ && let hir::Node::Item(item) = self.tcx.hir().get(hir_id)
+ && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item
+ && !matches!(abi, Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic)
+ {
+ return;
+ }
- diag.span_label(span, "not an `extern` block");
- diag.emit();
- });
+ self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+ let mut diag =
+ lint.build("attribute should be applied to an `extern` block with non-Rust ABI");
+ diag.warn(
+ "this was previously accepted by the compiler but is \
+ being phased out; it will become a hard error in \
+ a future release!",
+ );
+ if target != Target::ForeignMod {
+ diag.span_label(span, "not an `extern` block");
}
- }
+ diag.emit();
+ });
}
/// Checks if `#[link_name]` is applied to an item other than a foreign function or static.