Documentation and finishing touches
This commit is contained in:
parent
ba9f51b055
commit
8586cad77c
4 changed files with 215 additions and 79 deletions
|
@ -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)]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue