Documentation and finishing touches

This commit is contained in:
mejrs 2025-03-29 02:04:10 +01:00
parent ba9f51b055
commit 8586cad77c
4 changed files with 215 additions and 79 deletions

View file

@ -1270,6 +1270,25 @@ impl DesugaringKind {
DesugaringKind::PatTyRange => "pattern type",
}
}
/// For use with `rustc_unimplemented` to support conditions
/// like `from_desugaring = "QuestionMark"`
pub fn matches(&self, value: &str) -> bool {
match self {
DesugaringKind::CondTemporary => value == "CondTemporary",
DesugaringKind::Async => value == "Async",
DesugaringKind::Await => value == "Await",
DesugaringKind::QuestionMark => value == "QuestionMark",
DesugaringKind::TryBlock => value == "TryBlock",
DesugaringKind::YeetExpr => value == "YeetExpr",
DesugaringKind::OpaqueTy => value == "OpaqueTy",
DesugaringKind::ForLoop => value == "ForLoop",
DesugaringKind::WhileLoop => value == "WhileLoop",
DesugaringKind::BoundModifier => value == "BoundModifier",
DesugaringKind::Contract => value == "Contract",
DesugaringKind::PatTyRange => value == "PatTyRange",
}
}
}
#[derive(Default)]

View file

@ -6,6 +6,7 @@ use rustc_errors::codes::*;
use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{AttrArgs, Attribute};
use rustc_macros::LintDiagnostic;
use rustc_middle::bug;
use rustc_middle::ty::print::PrintTraitRefExt;
use rustc_middle::ty::{self, GenericArgsRef, GenericParamDef, GenericParamDefKind, TyCtxt};
@ -17,7 +18,6 @@ use {rustc_attr_parsing as attr, rustc_hir as hir};
use super::{ObligationCauseCode, PredicateObligation};
use crate::error_reporting::TypeErrCtxt;
use crate::error_reporting::traits::on_unimplemented_condition::{Condition, ConditionOptions};
use crate::error_reporting::traits::on_unimplemented_format::errors::*;
use crate::error_reporting::traits::on_unimplemented_format::{Ctx, FormatArgs, FormatString};
use crate::errors::{
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
@ -112,10 +112,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
// FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs,
// but I guess we could synthesize one here. We don't see any errors that rely on
// that yet, though.
let item_context = self
.describe_enclosure(obligation.cause.body_id)
.map(|t| t.to_owned())
.unwrap_or(String::new());
let item_context = self.describe_enclosure(obligation.cause.body_id).unwrap_or("");
let direct = match obligation.cause.code() {
ObligationCauseCode::BuiltinDerived(..)
@ -128,7 +125,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
};
let from_desugaring = obligation.cause.span.desugaring_kind().map(|k| format!("{k:?}"));
let from_desugaring = obligation.cause.span.desugaring_kind();
let cause = if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
Some("MainFunctionType".to_string())
@ -253,8 +250,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
}));
let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id).to_string();
let trait_sugared = trait_pred.trait_ref.print_trait_sugared().to_string();
let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id);
let trait_sugared = trait_pred.trait_ref.print_trait_sugared();
let condition_options = ConditionOptions {
self_types,
@ -268,6 +265,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
// Unlike the generic_args earlier,
// this one is *not* collected under `with_no_trimmed_paths!`
// for printing the type to the user
//
// This includes `Self`, as it is the first parameter in `own_params`.
let generic_args = self
.tcx
.generics_of(trait_pred.trait_ref.def_id)
@ -341,6 +340,63 @@ pub enum AppendConstMessage {
Custom(Symbol, Span),
}
#[derive(LintDiagnostic)]
#[diag(trait_selection_malformed_on_unimplemented_attr)]
#[help]
pub struct MalformedOnUnimplementedAttrLint {
#[label]
pub span: Span,
}
impl MalformedOnUnimplementedAttrLint {
pub fn new(span: Span) -> Self {
Self { span }
}
}
#[derive(LintDiagnostic)]
#[diag(trait_selection_missing_options_for_on_unimplemented_attr)]
#[help]
pub struct MissingOptionsForOnUnimplementedAttr;
#[derive(LintDiagnostic)]
#[diag(trait_selection_ignored_diagnostic_option)]
pub struct IgnoredDiagnosticOption {
pub option_name: &'static str,
#[label]
pub span: Span,
#[label(trait_selection_other_label)]
pub prev_span: Span,
}
impl IgnoredDiagnosticOption {
pub fn maybe_emit_warning<'tcx>(
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
new: Option<Span>,
old: Option<Span>,
option_name: &'static str,
) {
if let (Some(new_item), Some(old_item)) = (new, old) {
if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id),
new_item,
IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name },
);
}
}
}
}
#[derive(LintDiagnostic)]
#[diag(trait_selection_wrapped_parser_error)]
pub struct WrappedParserError {
pub description: String,
pub label: String,
}
impl<'tcx> OnUnimplementedDirective {
fn parse(
tcx: TyCtxt<'tcx>,
@ -664,7 +720,7 @@ impl<'tcx> OnUnimplementedDirective {
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
condition_options: &ConditionOptions,
args: &FormatArgs,
args: &FormatArgs<'tcx>,
) -> OnUnimplementedNote {
let mut message = None;
let mut label = None;
@ -784,7 +840,7 @@ impl<'tcx> OnUnimplementedFormatString {
&self,
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
args: &FormatArgs,
args: &FormatArgs<'tcx>,
) -> String {
let trait_def_id = trait_ref.def_id;
let ctx = if self.is_diagnostic_namespace_variant {

View file

@ -2,19 +2,10 @@ use rustc_ast::MetaItemInner;
use rustc_attr_parsing as attr;
use rustc_middle::ty::{self, TyCtxt};
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
use rustc_span::{Span, Symbol, kw, sym};
pub static ALLOWED_CONDITION_SYMBOLS: &[Symbol] = &[
sym::from_desugaring,
sym::direct,
sym::cause,
sym::integral,
sym::integer_,
sym::float,
sym::_Self,
sym::crate_local,
];
use rustc_span::{DesugaringKind, Span, Symbol, kw, sym};
/// A predicate in an attribute using on, all, any,
/// similar to a cfg predicate.
#[derive(Debug)]
pub struct Condition {
pub inner: MetaItemInner,
@ -31,8 +22,7 @@ impl Condition {
// `with_no_visible_paths` is also used when generating the options,
// so we need to match it here.
ty::print::with_no_visible_paths!({
let mut parser = Parser::new(v.as_str(), None, None, false, ParseMode::Format);
let constructed_message = (&mut parser)
Parser::new(v.as_str(), None, None, false, ParseMode::Format)
.map(|p| match p {
Piece::Lit(s) => s.to_owned(),
Piece::NextArgument(a) => match a.position {
@ -47,8 +37,7 @@ impl Condition {
Position::ArgumentIs(idx) => format!("{{{idx}}}"),
},
})
.collect();
constructed_message
.collect()
})
});
@ -57,13 +46,57 @@ impl Condition {
}
}
/// Used with `Condition::matches_predicate` to test whether the condition applies
///
/// For example, given a
/// ```rust,ignore (just an example)
/// #[rustc_on_unimplemented(
/// on(all(from_desugaring = "QuestionMark"),
/// message = "the `?` operator can only be used in {ItemContext} \
/// that returns `Result` or `Option` \
/// (or another type that implements `{FromResidual}`)",
/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`",
/// parent_label = "this function should return `Result` or `Option` to accept `?`"
/// ),
/// )]
/// pub trait FromResidual<R = <Self as Try>::Residual> {
/// ...
/// }
///
/// async fn an_async_function() -> u32 {
/// let x: Option<u32> = None;
/// x?; //~ ERROR the `?` operator
/// 22
/// }
/// ```
/// it will look like this:
///
/// ```rust,ignore (just an example)
/// ConditionOptions {
/// self_types: ["u32", "{integral}"],
/// from_desugaring: Some("QuestionMark"),
/// cause: None,
/// crate_local: false,
/// direct: true,
/// generic_args: [("Self","u32"),
/// ("R", "core::option::Option<core::convert::Infallible>"),
/// ("R", "core::option::Option<T>" ),
/// ],
/// }
/// ```
#[derive(Debug)]
pub struct ConditionOptions {
/// All the self types that may apply.
/// for example
pub self_types: Vec<String>,
pub from_desugaring: Option<String>,
// The kind of compiler desugaring.
pub from_desugaring: Option<DesugaringKind>,
/// Match on a variant of [rustc_infer::traits::ObligationCauseCode]
pub cause: Option<String>,
pub crate_local: bool,
/// Is the obligation "directly" user-specified, rather than derived?
pub direct: bool,
// A list of the generic arguments and their reified types
pub generic_args: Vec<(Symbol, String)>,
}
@ -74,7 +107,7 @@ impl ConditionOptions {
// from_desugaring as a flag
(sym::from_desugaring, None) => self.from_desugaring.is_some(),
// from_desugaring as key == value
(sym::from_desugaring, v) => *v == self.from_desugaring,
(sym::from_desugaring, Some(v)) if let Some(ds) = self.from_desugaring => ds.matches(v),
(sym::cause, Some(value)) => self.cause.as_deref() == Some(value),
(sym::crate_local, None) => self.crate_local,
(sym::direct, None) => self.direct,

View file

@ -1,5 +1,8 @@
use std::fmt;
use errors::*;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::print::TraitRefPrintSugared;
use rustc_parse_format::{
Alignment, Argument, Count, FormatSpec, InnerSpan, ParseError, ParseMode, Parser,
Piece as RpfPiece, Position,
@ -8,28 +11,39 @@ use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
use rustc_span::def_id::DefId;
use rustc_span::{BytePos, Pos, Span, Symbol, kw, sym};
#[allow(dead_code)]
/// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces",
/// either as string pieces or dynamic arguments.
#[derive(Debug)]
pub struct FormatString {
#[allow(dead_code, reason = "Debug impl")]
input: Symbol,
input_span: Span,
span: Span,
pieces: Vec<Piece>,
// the formatting string was parsed succesfully but with warnings
/// The formatting string was parsed succesfully but with warnings
pub warnings: Vec<FormatWarning>,
}
#[derive(Debug)]
enum Piece {
Lit(String),
Arg(FormatArg),
}
pub enum FormatArg {
#[derive(Debug)]
enum FormatArg {
// A generic parameter, like `{T}` if we're on the `From<T>` trait.
GenericParam { generic_param: Symbol, span: Span },
GenericParam {
generic_param: Symbol,
},
// `{Self}`
SelfUpper,
/// `{This}` or `{TraitName}`
This,
/// The sugared form of the trait
Trait,
/// what we're in, like a function, method, closure etc.
ItemContext,
/// What the user typed, if it doesn't match anything we can use.
AsIs(String),
}
@ -40,6 +54,7 @@ pub enum Ctx<'tcx> {
DiagnosticOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId },
}
#[derive(Debug)]
pub enum FormatWarning {
UnknownParam { argument_name: Symbol, span: Span },
PositionalArgument { span: Span, help: String },
@ -95,26 +110,58 @@ impl FormatWarning {
}
}
/// Arguments to fill a [FormatString] with.
///
/// For example, given a
/// ```rust,ignore (just an example)
///
/// #[rustc_on_unimplemented(
/// on(all(from_desugaring = "QuestionMark"),
/// message = "the `?` operator can only be used in {ItemContext} \
/// that returns `Result` or `Option` \
/// (or another type that implements `{FromResidual}`)",
/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`",
/// parent_label = "this function should return `Result` or `Option` to accept `?`"
/// ),
/// )]
/// pub trait FromResidual<R = <Self as Try>::Residual> {
/// ...
/// }
///
/// async fn an_async_function() -> u32 {
/// let x: Option<u32> = None;
/// x?; //~ ERROR the `?` operator
/// 22
/// }
/// ```
/// it will look like this:
///
/// ```rust,ignore (just an example)
/// FormatArgs {
/// this: "FromResidual",
/// trait_sugared: "FromResidual<Option<Infallible>>",
/// item_context: "an async function",
/// generic_args: [("Self", "u32"), ("R", "Option<Infallible>")],
/// }
/// ```
#[derive(Debug)]
pub struct ConditionOptions {
pub self_types: Vec<String>,
pub from_desugaring: Option<String>,
pub cause: Option<String>,
pub crate_local: bool,
pub direct: bool,
pub generic_args: Vec<(Symbol, String)>,
}
#[derive(Debug)]
pub struct FormatArgs {
pub struct FormatArgs<'tcx> {
pub this: String,
pub trait_sugared: String,
pub item_context: String,
pub trait_sugared: TraitRefPrintSugared<'tcx>,
pub item_context: &'static str,
pub generic_args: Vec<(Symbol, String)>,
}
impl FormatString {
pub fn parse(input: Symbol, input_span: Span, ctx: &Ctx<'_>) -> Result<Self, Vec<ParseError>> {
pub fn span(&self) -> Span {
self.span
}
pub fn parse<'tcx>(
input: Symbol,
span: Span,
ctx: &Ctx<'tcx>,
) -> Result<Self, Vec<ParseError>> {
let s = input.as_str();
let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
let mut pieces = Vec::new();
@ -126,28 +173,28 @@ impl FormatString {
pieces.push(Piece::Lit(lit.into()));
}
RpfPiece::NextArgument(arg) => {
warn_on_format_spec(arg.format, &mut warnings, input_span);
let arg = parse_arg(&arg, ctx, &mut warnings, input_span);
warn_on_format_spec(arg.format, &mut warnings, span);
let arg = parse_arg(&arg, ctx, &mut warnings, span);
pieces.push(Piece::Arg(arg));
}
}
}
if parser.errors.is_empty() {
Ok(FormatString { input, input_span, pieces, warnings })
Ok(FormatString { input, pieces, span, warnings })
} else {
Err(parser.errors)
}
}
pub fn format(&self, args: &FormatArgs) -> String {
pub fn format(&self, args: &FormatArgs<'_>) -> String {
let mut ret = String::new();
for piece in &self.pieces {
match piece {
Piece::Lit(s) | Piece::Arg(FormatArg::AsIs(s)) => ret.push_str(&s),
// `A` if we have `trait Trait<A> {}` and `note = "i'm the actual type of {A}"`
Piece::Arg(FormatArg::GenericParam { generic_param, .. }) => {
Piece::Arg(FormatArg::GenericParam { generic_param }) => {
// Should always be some but we can't raise errors here
let value = match args.generic_args.iter().find(|(p, _)| p == generic_param) {
Some((_, val)) => val.to_string(),
@ -166,17 +213,19 @@ impl FormatString {
// It's only `rustc_onunimplemented` from here
Piece::Arg(FormatArg::This) => ret.push_str(&args.this),
Piece::Arg(FormatArg::Trait) => ret.push_str(&args.trait_sugared),
Piece::Arg(FormatArg::ItemContext) => ret.push_str(&args.item_context),
Piece::Arg(FormatArg::Trait) => {
let _ = fmt::write(&mut ret, format_args!("{}", &args.trait_sugared));
}
Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context),
}
}
ret
}
}
fn parse_arg(
fn parse_arg<'tcx>(
arg: &Argument<'_>,
ctx: &Ctx<'_>,
ctx: &Ctx<'tcx>,
warnings: &mut Vec<FormatWarning>,
input_span: Span,
) -> FormatArg {
@ -233,7 +282,7 @@ fn parse_arg(
Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. },
generic_param,
) if generics.own_params.iter().any(|param| param.name == generic_param) => {
FormatArg::GenericParam { generic_param, span }
FormatArg::GenericParam { generic_param }
}
(_, argument_name) => {
@ -286,6 +335,7 @@ fn warn_on_format_spec(spec: FormatSpec<'_>, warnings: &mut Vec<FormatWarning>,
}
}
/// Helper function because `Span` and `rustc_parse_format::InnerSpan` don't know about each other
fn slice_span(input: Span, inner: InnerSpan) -> Span {
let InnerSpan { start, end } = inner;
let span = input.data();
@ -300,8 +350,6 @@ fn slice_span(input: Span, inner: InnerSpan) -> Span {
pub mod errors {
use rustc_macros::LintDiagnostic;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
use rustc_span::Ident;
use super::*;
@ -324,26 +372,6 @@ pub mod errors {
#[help]
pub struct InvalidFormatSpecifier;
#[derive(LintDiagnostic)]
#[diag(trait_selection_wrapped_parser_error)]
pub struct WrappedParserError {
pub description: String,
pub label: String,
}
#[derive(LintDiagnostic)]
#[diag(trait_selection_malformed_on_unimplemented_attr)]
#[help]
pub struct MalformedOnUnimplementedAttrLint {
#[label]
pub span: Span,
}
impl MalformedOnUnimplementedAttrLint {
pub fn new(span: Span) -> Self {
Self { span }
}
}
#[derive(LintDiagnostic)]
#[diag(trait_selection_missing_options_for_on_unimplemented_attr)]
#[help]