Skip to content
Open
28 changes: 20 additions & 8 deletions compiler/rustc_codegen_ssa/src/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use rustc_hir::attrs::{
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::{self as hir, Attribute, find_attr};
use rustc_macros::Diagnostic;
use rustc_middle::middle::codegen_fn_attrs::{
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs,
};
Expand Down Expand Up @@ -385,6 +386,17 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
}
}

#[derive(Diagnostic)]
#[diag("non-default `sanitize` will have no effect after inlining")]
struct SanitizeOnInline {
#[note("inlining requested here")]
inline_span: Span,
}

#[derive(Diagnostic)]
#[diag("the async executor can run blocking code, without realtime sanitizer catching it")]
struct AsyncBlocking;

fn check_result(
tcx: TyCtxt<'_>,
did: LocalDefId,
Expand Down Expand Up @@ -425,10 +437,12 @@ fn check_result(
(interesting_spans.sanitize, interesting_spans.inline)
{
let hir_id = tcx.local_def_id_to_hir_id(did);
tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, sanitize_span, |lint| {
lint.primary_message("non-default `sanitize` will have no effect after inlining");
lint.span_note(inline_span, "inlining requested here");
})
tcx.emit_node_span_lint(
lint::builtin::INLINE_NO_SANITIZE,
hir_id,
sanitize_span,
SanitizeOnInline { inline_span },
)
}

// warn for nonblocking async functions, blocks and closures.
Expand All @@ -445,13 +459,11 @@ fn check_result(
!= rustc_hir::ClosureKind::Closure))
{
let hir_id = tcx.local_def_id_to_hir_id(did);
tcx.node_span_lint(
tcx.emit_node_span_lint(
lint::builtin::RTSAN_NONBLOCKING_ASYNC,
hir_id,
sanitize_span,
|lint| {
lint.primary_message(r#"the async executor can run blocking code, without realtime sanitizer catching it"#);
}
AsyncBlocking,
);
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
pub trait Diagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> {
/// Write out as a diagnostic out of `DiagCtxt`.
#[must_use]
#[track_caller]
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G>;
}

Expand Down
99 changes: 66 additions & 33 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ use std::ops::ControlFlow;
use rustc_abi::{ExternAbi, FieldIdx, ScalableElt};
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::codes::*;
use rustc_errors::{EmissionGuarantee, MultiSpan};
use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan};
use rustc_hir as hir;
use rustc_hir::attrs::ReprAttr::ReprPacked;
use rustc_hir::def::{CtorKind, DefKind};
use rustc_hir::{LangItem, Node, find_attr, intravisit};
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
use rustc_infer::traits::{Obligation, ObligationCauseCode, WellFormedLoc};
use rustc_lint_defs::builtin::{REPR_TRANSPARENT_NON_ZST_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS};
use rustc_macros::Diagnostic;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
use rustc_middle::middle::stability::EvalResult;
Expand Down Expand Up @@ -53,6 +54,22 @@ fn add_abi_diag_help<T: EmissionGuarantee>(abi: ExternAbi, diag: &mut Diag<'_, T
}

pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: ExternAbi) {
struct UnsupportedCallingConventions {
abi: ExternAbi,
}

impl<'a> Diagnostic<'a, ()> for UnsupportedCallingConventions {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
let Self { abi } = self;
let mut lint = Diag::new(
dcx,
level,
format!("{abi} is not a supported ABI for the current target"),
);
add_abi_diag_help(abi, &mut lint);
lint
}
}
// FIXME: This should be checked earlier, e.g. in `rustc_ast_lowering`, as this
// currently only guards function imports, function definitions, and function pointer types.
// Functions in trait declarations can still use "deprecated" ABIs without any warning.
Expand All @@ -64,12 +81,12 @@ pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: ExternAbi
tcx.dcx().span_delayed_bug(span, format!("{abi} should be rejected in ast_lowering"));
}
AbiMapping::Deprecated(..) => {
tcx.node_span_lint(UNSUPPORTED_CALLING_CONVENTIONS, hir_id, span, |lint| {
lint.primary_message(format!(
"{abi} is not a supported ABI for the current target"
));
add_abi_diag_help(abi, lint);
});
tcx.emit_node_span_lint(
UNSUPPORTED_CALLING_CONVENTIONS,
hir_id,
span,
UnsupportedCallingConventions { abi },
);
}
}
}
Expand Down Expand Up @@ -174,6 +191,11 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b

/// Check that a `static` is inhabited.
fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
#[derive(Diagnostic)]
#[diag("static of uninhabited type")]
#[note("uninhabited statics cannot be initialized, and any access would be an immediate error")]
struct StaticOfUninhabitedType;

// Make sure statics are inhabited.
// Other parts of the compiler assume that there are no uninhabited places. In principle it
// would be enough to check this for `extern` statics, as statics with an initializer will
Expand Down Expand Up @@ -204,15 +226,11 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
}
};
if layout.is_uninhabited() {
tcx.node_span_lint(
tcx.emit_node_span_lint(
UNINHABITED_STATIC,
tcx.local_def_id_to_hir_id(def_id),
span,
|lint| {
lint.primary_message("static of uninhabited type");
lint
.note("uninhabited statics cannot be initialized, and any access would be an immediate error");
},
StaticOfUninhabitedType,
);
}
}
Expand Down Expand Up @@ -1637,6 +1655,39 @@ pub(super) fn check_packed_inner(
}

pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) {
struct ZeroSizedFieldReprTransparentIncompatibility<'tcx> {
unsuited: UnsuitedInfo<'tcx>,
}

impl<'a, 'tcx> Diagnostic<'a, ()> for ZeroSizedFieldReprTransparentIncompatibility<'tcx> {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
let Self { unsuited } = self;
let (title, note) = match unsuited.reason {
UnsuitedReason::NonExhaustive => (
"external non-exhaustive types",
"is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.",
),
UnsuitedReason::PrivateField => (
"external types with private fields",
"contains private fields, so it could become non-zero-sized in the future.",
),
UnsuitedReason::ReprC => (
"`repr(C)` types",
"is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets.",
),
};
Diag::new(
dcx,
level,
format!("zero-sized fields in `repr(transparent)` cannot contain {title}"),
)
.with_note(format!(
"this field contains `{field_ty}`, which {note}",
field_ty = unsuited.ty,
))
}
}

if !adt.repr().transparent() {
return;
}
Expand Down Expand Up @@ -1747,29 +1798,11 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
// If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
// Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
if non_trivial_count > 0 || prev_unsuited_1zst {
tcx.node_span_lint(
tcx.emit_node_span_lint(
REPR_TRANSPARENT_NON_ZST_FIELDS,
tcx.local_def_id_to_hir_id(adt.did().expect_local()),
field.span,
|lint| {
let title = match unsuited.reason {
UnsuitedReason::NonExhaustive => "external non-exhaustive types",
UnsuitedReason::PrivateField => "external types with private fields",
UnsuitedReason::ReprC => "`repr(C)` types",
};
lint.primary_message(
format!("zero-sized fields in `repr(transparent)` cannot contain {title}"),
);
let note = match unsuited.reason {
UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.",
UnsuitedReason::PrivateField => "contains private fields, so it could become non-zero-sized in the future.",
UnsuitedReason::ReprC => "is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets.",
};
lint.note(format!(
"this field contains `{field_ty}`, which {note}",
field_ty = unsuited.ty,
));
},
ZeroSizedFieldReprTransparentIncompatibility { unsuited },
);
} else {
prev_unsuited_1zst = true;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ use std::num::NonZero;
pub use check::{check_abi, check_custom_abi};
use rustc_abi::VariantIdx;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err};
use rustc_errors::{ErrorGuaranteed, pluralize, struct_span_code_err};
use rustc_hir::LangItem;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor;
Expand Down
31 changes: 24 additions & 7 deletions compiler/rustc_hir_analysis/src/check_unused.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
use rustc_data_structures::unord::{ExtendUnord, UnordSet};
use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint;
use rustc_span::Span;
use tracing::debug;

struct UnusedImport<'tcx> {
tcx: TyCtxt<'tcx>,
span: Span,
}

impl<'a, 'tcx> Diagnostic<'a, ()> for UnusedImport<'tcx> {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
let Self { tcx, span } = self;
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) {
Diag::new(dcx, level, format!("unused import: `{snippet}`"))
} else {
Diag::new(dcx, level, "unused import")
}
}
}

pub(super) fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) {
let mut used_trait_imports = UnordSet::<LocalDefId>::default();

Expand All @@ -31,12 +49,11 @@ pub(super) fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) {
continue;
}
let (path, _) = item.expect_use();
tcx.node_span_lint(lint::builtin::UNUSED_IMPORTS, item.hir_id(), path.span, |lint| {
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) {
lint.primary_message(format!("unused import: `{snippet}`"));
} else {
lint.primary_message("unused import");
}
});
tcx.emit_node_span_lint(
lint::builtin::UNUSED_IMPORTS,
item.hir_id(),
path.span,
UnusedImport { tcx, span: path.span },
);
}
}
25 changes: 18 additions & 7 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ use rustc_abi::{ExternAbi, Size};
use rustc_ast::Recovered;
use rustc_data_structures::assert_matches;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, E0228, ErrorGuaranteed, StashKey};
use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, Diagnostic, E0228, ErrorGuaranteed, Level, StashKey,
};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt};
Expand Down Expand Up @@ -610,6 +612,19 @@ pub(super) fn lower_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) {
}

pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: LocalDefId) {
struct ReprCIssue {
msg: &'static str,
}

impl<'a> Diagnostic<'a, ()> for ReprCIssue {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
let Self { msg } = self;
Diag::new(dcx, level, msg)
.with_note("`repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C")
.with_help("use `repr($int_ty)` instead to explicitly set the size of this enum")
}
}

let def = tcx.adt_def(def_id);
let repr_type = def.repr().discr_type();
let initial = repr_type.initial_discriminant(tcx);
Expand Down Expand Up @@ -659,15 +674,11 @@ pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: LocalDefId) {
} else {
"`repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int`"
};
tcx.node_span_lint(
tcx.emit_node_span_lint(
rustc_session::lint::builtin::REPR_C_ENUMS_LARGER_THAN_INT,
tcx.local_def_id_to_hir_id(def_id),
span,
|d| {
d.primary_message(msg)
.note("`repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C")
.help("use `repr($int_ty)` instead to explicitly set the size of this enum");
}
ReprCIssue { msg },
);
}
}
Expand Down
18 changes: 14 additions & 4 deletions compiler/rustc_hir_analysis/src/collect/generics_of.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::ops::ControlFlow;

use rustc_data_structures::assert_matches;
use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, Visitor, VisitorExt};
Expand All @@ -17,6 +18,17 @@ use crate::middle::resolve_bound_vars as rbv;
pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
use rustc_hir::*;

struct GenericParametersForbiddenHere {
msg: &'static str,
}

impl<'a> Diagnostic<'a, ()> for GenericParametersForbiddenHere {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
let Self { msg } = self;
Diag::new(dcx, level, msg)
}
}

// For an RPITIT, synthesize generics which are equal to the opaque's generics
// and parent fn's generics compressed into one list.
if let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id }) =
Expand Down Expand Up @@ -269,13 +281,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
match param_default_policy.expect("no policy for generic param default") {
ParamDefaultPolicy::Allowed => {}
ParamDefaultPolicy::FutureCompatForbidden => {
tcx.node_span_lint(
tcx.emit_node_span_lint(
lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
param.hir_id,
param.span,
|lint| {
lint.primary_message(MESSAGE);
},
GenericParametersForbiddenHere { msg: MESSAGE },
);
}
ParamDefaultPolicy::Forbidden => {
Expand Down
Loading
Loading