1
Fork 0

lint: revamp ImproperCTypes diagnostic architecture for nested notes and help messages

This commit is contained in:
niacdoial 2024-11-05 22:54:48 +01:00
parent c94848c046
commit f021d99cc8
2 changed files with 146 additions and 45 deletions

View file

@ -1851,13 +1851,44 @@ pub(crate) struct UnpredictableFunctionPointerComparisonsSuggestion<'a> {
pub right: Span,
}
pub(crate) struct ImproperCTypesLayer<'a> {
pub ty: Ty<'a>,
pub inner_ty: Option<Ty<'a>>,
pub note: DiagMessage,
pub span_note: Option<Span>,
pub help: Option<DiagMessage>,
}
impl<'a> Subdiagnostic for ImproperCTypesLayer<'a> {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self,
diag: &mut Diag<'_, G>,
f: &F,
) {
diag.arg("ty", self.ty);
if let Some(ty) = self.inner_ty {
diag.arg("inner_ty", ty);
}
if let Some(help) = self.help {
let msg = f(diag, help.into());
diag.help(msg);
}
let msg = f(diag, self.note.into());
diag.note(msg);
if let Some(note) = self.span_note {
let msg = f(diag, fluent::lint_note.into());
diag.span_note(note, msg);
};
}
}
pub(crate) struct ImproperCTypes<'a> {
pub ty: Ty<'a>,
pub desc: &'a str,
pub label: Span,
pub help: Option<DiagMessage>,
pub note: DiagMessage,
pub span_note: Option<Span>,
pub reasons: Vec<ImproperCTypesLayer<'a>>,
}
// Used because of the complexity of Option<DiagMessage>, DiagMessage, and Option<Span>
@ -1867,12 +1898,8 @@ impl<'a> LintDiagnostic<'a, ()> for ImproperCTypes<'_> {
diag.arg("ty", self.ty);
diag.arg("desc", self.desc);
diag.span_label(self.label, fluent::lint_label);
if let Some(help) = self.help {
diag.help(help);
}
diag.note(self.note);
if let Some(note) = self.span_note {
diag.span_note(note, fluent::lint_note);
for reason in self.reasons.into_iter() {
diag.subdiagnostic(reason);
}
}
}

View file

@ -22,10 +22,10 @@ mod improper_ctypes;
use crate::lints::{
AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons,
VariantSizeDifferencesDiag,
AtomicOrderingStore, ImproperCTypes, ImproperCTypesLayer, InvalidAtomicOrderingDiag,
InvalidNanComparisons, InvalidNanComparisonsSuggestion,
UnpredictableFunctionPointerComparisons, UnpredictableFunctionPointerComparisonsSuggestion,
UnusedComparisons, VariantSizeDifferencesDiag,
};
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
@ -727,7 +727,19 @@ struct CTypesVisitorState<'tcx> {
enum FfiResult<'tcx> {
FfiSafe,
FfiPhantom(Ty<'tcx>),
FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> },
FfiUnsafe {
ty: Ty<'tcx>,
reason: DiagMessage,
help: Option<DiagMessage>,
},
// NOTE: this `allow` is only here for one retroactively-added commit
#[allow(dead_code)]
FfiUnsafeWrapper {
ty: Ty<'tcx>,
reason: DiagMessage,
help: Option<DiagMessage>,
wrapped: Box<FfiResult<'tcx>>,
},
}
pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
@ -933,12 +945,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
/// Check if the type is array and emit an unsafe type lint.
fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
if let ty::Array(..) = ty.kind() {
self.emit_ffi_unsafe_type_lint(
self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
ty,
sp,
fluent::lint_improper_ctypes_array_reason,
Some(fluent::lint_improper_ctypes_array_help),
);
note: fluent::lint_improper_ctypes_array_reason,
help: Some(fluent::lint_improper_ctypes_array_help),
inner_ty: None,
span_note: None,
}]);
true
} else {
false
@ -995,9 +1008,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
all_phantom &= match self.check_field_type_for_ffi(acc, field, args) {
FfiSafe => false,
// `()` fields are FFI-safe!
FfiUnsafe { ty, .. } if ty.is_unit() => false,
FfiUnsafe { ty, .. } | FfiUnsafeWrapper { ty, .. } if ty.is_unit() => false,
FfiPhantom(..) => true,
r @ FfiUnsafe { .. } => return r,
r @ (FfiUnsafe { .. } | FfiUnsafeWrapper { .. }) => return r,
}
}
@ -1278,8 +1291,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
&mut self,
ty: Ty<'tcx>,
sp: Span,
note: DiagMessage,
help: Option<DiagMessage>,
mut reasons: Vec<ImproperCTypesLayer<'tcx>>,
) {
let lint = match self.mode {
CItemKind::Declaration => IMPROPER_CTYPES,
@ -1289,21 +1301,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
CItemKind::Declaration => "block",
CItemKind::Definition => "fn",
};
let span_note = if let ty::Adt(def, _) = ty.kind()
&& let Some(sp) = self.cx.tcx.hir().span_if_local(def.did())
{
Some(sp)
} else {
None
};
self.cx.emit_span_lint(lint, sp, ImproperCTypes {
ty,
desc,
label: sp,
help,
note,
span_note,
});
for reason in reasons.iter_mut() {
reason.span_note = if let ty::Adt(def, _) = reason.ty.kind()
&& let Some(sp) = self.cx.tcx.hir().span_if_local(def.did())
{
Some(sp)
} else {
None
};
}
self.cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, reasons });
}
fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
@ -1332,7 +1340,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
.visit_with(&mut ProhibitOpaqueTypes)
.break_value()
{
self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None);
self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
ty,
note: fluent::lint_improper_ctypes_opaque,
span_note: Some(sp),
help: None,
inner_ty: None,
}]);
true
} else {
false
@ -1371,15 +1385,75 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
match self.check_type_for_ffi(&mut acc, ty) {
FfiResult::FfiSafe => {}
FfiResult::FfiPhantom(ty) => {
self.emit_ffi_unsafe_type_lint(
self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
ty,
sp,
fluent::lint_improper_ctypes_only_phantomdata,
None,
);
note: fluent::lint_improper_ctypes_only_phantomdata,
span_note: None, // filled later
help: None,
inner_ty: None,
}]);
}
FfiResult::FfiUnsafe { ty, reason, help } => {
self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
ty,
help,
note: reason,
span_note: None, // filled later
inner_ty: None,
}]);
}
ffir @ FfiResult::FfiUnsafeWrapper { .. } => {
let mut last_ty = None;
let mut ffiresult_recursor = Some(&ffir);
let mut cimproper_layers: Vec<ImproperCTypesLayer<'tcx>> = vec![];
// this whole while block converts the arbitrarily-deep
// FfiResult stack to a ImproperCTypesLayer Vec
while let Some(ref ffir_rec) = ffiresult_recursor {
match ffir_rec {
FfiResult::FfiPhantom(ty) => {
last_ty = Some(ty.clone());
let len = cimproper_layers.len();
if len > 0 {
cimproper_layers[len - 1].inner_ty = last_ty.clone();
}
cimproper_layers.push(ImproperCTypesLayer {
ty: ty.clone(),
inner_ty: None,
help: None,
note: fluent::lint_improper_ctypes_only_phantomdata,
span_note: None, // filled later
});
ffiresult_recursor = None;
}
FfiResult::FfiUnsafe { ty, reason, help }
| FfiResult::FfiUnsafeWrapper { ty, reason, help, .. } => {
last_ty = Some(ty.clone());
let len = cimproper_layers.len();
if len > 0 {
cimproper_layers[len - 1].inner_ty = last_ty.clone();
}
cimproper_layers.push(ImproperCTypesLayer {
ty: ty.clone(),
inner_ty: None,
help: help.clone(),
note: reason.clone(),
span_note: None, // filled later
});
if let FfiResult::FfiUnsafeWrapper { wrapped, .. } = ffir_rec {
ffiresult_recursor = Some(wrapped.as_ref());
} else {
ffiresult_recursor = None;
}
}
FfiResult::FfiSafe => {
bug!("malformed FfiResult stack: it should be unsafe all the way down")
}
};
}
let last_ty = last_ty.unwrap(); // populated by any run of the `while` block
self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers);
}
}
}