about summary refs log tree commit diff
diff options
context:
space:
mode:
authormejrs <59372212+mejrs@users.noreply.github.com>2025-03-29 02:04:10 +0100
committermejrs <59372212+mejrs@users.noreply.github.com>2025-04-14 00:12:36 +0200
commit8586cad77c175a0c2ce11c0579c537aa195e6da2 (patch)
tree77543adebc1609d910e85e3edfaeebe29748e1bb
parentba9f51b05503a567cb637db03eaa943c59e36922 (diff)
Documentation and finishing touches
-rw-r--r--compiler/rustc_span/src/hygiene.rs19
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs76
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs69
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs130
4 files changed, 215 insertions, 79 deletions
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index 9be16f8ce0c..f6d69da9951 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -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)]
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
index 7d8ecba3b2e..0478f3a7f11 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
@@ -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 {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs
index 491ba6d7ffc..116cfb01cb6 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs
@@ -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,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs
index 1bf15a34509..f835406122b 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs
@@ -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::*;
@@ -325,26 +373,6 @@ pub mod errors {
     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]
     pub struct MissingOptionsForOnUnimplementedAttr;