diff --git a/Cargo.lock b/Cargo.lock index 796e987fb7264..3a299f154e359 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3548,6 +3548,7 @@ dependencies = [ "rustc_lexer", "rustc_macros", "rustc_parse", + "rustc_parse_format", "rustc_session", "rustc_span", "rustc_target", @@ -4682,7 +4683,6 @@ dependencies = [ "rustc_macros", "rustc_middle", "rustc_next_trait_solver", - "rustc_parse_format", "rustc_session", "rustc_span", "rustc_transmute", diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 0a11a2da0dcf8..886df58e8d6f0 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } rustc_parse = { path = "../rustc_parse" } +rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 176af5cdd192e..2d2994c02cd61 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -347,3 +347,50 @@ impl CombineAttributeParser for FeatureParser { res } } + +pub(crate) struct RegisterToolParser; + +impl CombineAttributeParser for RegisterToolParser { + const PATH: &[Symbol] = &[sym::register_tool]; + type Item = Ident; + const CONVERT: ConvertFn = AttributeKind::RegisterTool; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const TEMPLATE: AttributeTemplate = template!(List: &["tool1, tool2, ..."]); + + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { + let ArgParser::List(list) = args else { + cx.expected_list(cx.attr_span, args); + return Vec::new(); + }; + + if list.is_empty() { + cx.warn_empty_attribute(cx.attr_span); + } + + let mut res = Vec::new(); + + for elem in list.mixed() { + let Some(elem) = elem.meta_item() else { + cx.expected_identifier(elem.span()); + continue; + }; + if let Err(arg_span) = elem.args().no_args() { + cx.expected_no_args(arg_span); + continue; + } + + let path = elem.path(); + let Some(ident) = path.word() else { + cx.expected_identifier(path.span()); + continue; + }; + + res.push(ident); + } + + res + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/do_not_recommend.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/do_not_recommend.rs similarity index 100% rename from compiler/rustc_attr_parsing/src/attributes/do_not_recommend.rs rename to compiler/rustc_attr_parsing/src/attributes/diagnostic/do_not_recommend.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs new file mode 100644 index 0000000000000..8f114b3284485 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -0,0 +1,536 @@ +use std::ops::Range; + +use rustc_errors::E0232; +use rustc_hir::AttrPath; +use rustc_hir::attrs::diagnostic::{ + AppendConstMessage, Directive, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, + Name, NameValue, OnUnimplementedCondition, Piece, Predicate, +}; +use rustc_hir::lints::{AttributeLintKind, FormatWarning}; +use rustc_macros::Diagnostic; +use rustc_parse_format::{ + Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position, +}; +use rustc_session::lint::builtin::{ + MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, +}; +use rustc_span::{Ident, InnerSpan, Span, Symbol, kw, sym}; +use thin_vec::{ThinVec, thin_vec}; + +use crate::context::{AcceptContext, Stage}; +use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser}; + +pub(crate) mod do_not_recommend; +pub(crate) mod on_const; +pub(crate) mod on_unimplemented; + +#[derive(Copy, Clone)] +pub(crate) enum Mode { + /// `#[rustc_on_unimplemented]` + RustcOnUnimplemented, + /// `#[diagnostic::on_unimplemented]` + DiagnosticOnUnimplemented, + /// `#[diagnostic::on_const]` + DiagnosticOnConst, +} + +fn merge_directives( + cx: &mut AcceptContext<'_, '_, S>, + first: &mut Option<(Span, Directive)>, + later: (Span, Directive), +) { + if let Some((_, first)) = first { + if first.is_rustc_attr || later.1.is_rustc_attr { + cx.emit_err(DupesNotAllowed); + } + + merge(cx, &mut first.message, later.1.message, sym::message); + merge(cx, &mut first.label, later.1.label, sym::label); + first.notes.extend(later.1.notes); + } else { + *first = Some(later); + } +} + +fn merge( + cx: &mut AcceptContext<'_, '_, S>, + first: &mut Option<(Span, T)>, + later: Option<(Span, T)>, + option_name: Symbol, +) { + match (first, later) { + (Some(_) | None, None) => {} + (Some((first_span, _)), Some((later_span, _))) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::IgnoredDiagnosticOption { + first_span: *first_span, + later_span, + option_name, + }, + later_span, + ); + } + (first @ None, Some(later)) => { + first.get_or_insert(later); + } + } +} + +fn parse_directive_items<'p, S: Stage>( + cx: &mut AcceptContext<'_, '_, S>, + mode: Mode, + items: impl Iterator, + is_root: bool, +) -> Option { + let condition = None; + let mut message: Option<(Span, _)> = None; + let mut label: Option<(Span, _)> = None; + let mut notes = ThinVec::new(); + let mut parent_label = None; + let mut subcommands = ThinVec::new(); + let mut append_const_msg = None; + + for item in items { + let span = item.span(); + + macro malformed() {{ + match mode { + Mode::RustcOnUnimplemented => { + cx.emit_err(NoValueInOnUnimplemented { span: item.span() }); + } + Mode::DiagnosticOnUnimplemented => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnUnimplementedAttr { span }, + span, + ); + } + Mode::DiagnosticOnConst => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnConstAttr { span }, + span, + ); + } + } + continue; + }} + + macro or_malformed($($code:tt)*) {{ + let Some(ret) = (||{ + Some($($code)*) + })() else { + + malformed!() + }; + ret + }} + + macro duplicate($name: ident, $($first_span:tt)*) {{ + match mode { + Mode::RustcOnUnimplemented => { + cx.emit_err(NoValueInOnUnimplemented { span: item.span() }); + } + Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::IgnoredDiagnosticOption { + first_span: $($first_span)*, + later_span: span, + option_name: $name, + }, + span, + ); + } + } + }} + + let item: &MetaItemParser = or_malformed!(item.meta_item()?); + let name = or_malformed!(item.ident()?).name; + + // Some things like `message = "message"` must have a value. + // But with things like `append_const_msg` that is optional. + let value: Option = match item.args().name_value() { + Some(nv) => Some(or_malformed!(nv.value_as_ident()?)), + None => None, + }; + + let mut parse_format = |input: Ident| { + let snippet = cx.sess.source_map().span_to_snippet(input.span).ok(); + let is_snippet = snippet.is_some(); + match parse_format_string(input.name, snippet, input.span, mode) { + Ok((f, warnings)) => { + for warning in warnings { + let (FormatWarning::InvalidSpecifier { span, .. } + | FormatWarning::PositionalArgument { span, .. }) = warning; + cx.emit_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + AttributeLintKind::MalformedDiagnosticFormat { warning }, + span, + ); + } + + f + } + Err(e) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + AttributeLintKind::DiagnosticWrappedParserError { + description: e.description, + label: e.label, + span: slice_span(input.span, e.span, is_snippet), + }, + input.span, + ); + // We could not parse the input, just use it as-is. + FormatString { + input: input.name, + span: input.span, + pieces: thin_vec![Piece::Lit(input.name)], + } + } + } + }; + match (mode, name) { + (_, sym::message) => { + let value = or_malformed!(value?); + if let Some(message) = &message { + duplicate!(name, message.0) + } else { + message = Some((item.span(), parse_format(value))); + } + } + (_, sym::label) => { + let value = or_malformed!(value?); + if let Some(label) = &label { + duplicate!(name, label.0) + } else { + label = Some((item.span(), parse_format(value))); + } + } + (_, sym::note) => { + let value = or_malformed!(value?); + notes.push(parse_format(value)) + } + + (Mode::RustcOnUnimplemented, sym::append_const_msg) => { + append_const_msg = if let Some(msg) = value { + Some(AppendConstMessage::Custom(msg.name, item.span())) + } else { + Some(AppendConstMessage::Default) + } + } + (Mode::RustcOnUnimplemented, sym::parent_label) => { + let value = or_malformed!(value?); + if parent_label.is_none() { + parent_label = Some(parse_format(value)); + } else { + duplicate!(name, span) + } + } + (Mode::RustcOnUnimplemented, sym::on) => { + if is_root { + let items = or_malformed!(item.args().list()?); + let mut iter = items.mixed(); + let condition: &MetaItemOrLitParser = match iter.next() { + Some(c) => c, + None => { + cx.emit_err(InvalidOnClause::Empty { span }); + continue; + } + }; + + let condition = parse_condition(condition); + + if items.len() < 2 { + // Something like `#[rustc_on_unimplemented(on(.., /* nothing */))]` + // There's a condition but no directive behind it, this is a mistake. + malformed!(); + } + + let mut directive = + or_malformed!(parse_directive_items(cx, mode, iter, false)?); + + match condition { + Ok(c) => { + directive.condition = Some(c); + subcommands.push(directive); + } + Err(e) => { + cx.emit_err(e); + } + } + } else { + malformed!(); + } + } + + _other => { + malformed!(); + } + } + } + + Some(Directive { + is_rustc_attr: matches!(mode, Mode::RustcOnUnimplemented), + condition, + subcommands, + message, + label, + notes, + parent_label, + append_const_msg, + }) +} + +pub(crate) fn parse_format_string( + input: Symbol, + snippet: Option, + span: Span, + mode: Mode, +) -> Result<(FormatString, Vec), ParseError> { + let s = input.as_str(); + let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic); + let pieces: Vec<_> = parser.by_ref().collect(); + + if let Some(err) = parser.errors.into_iter().next() { + return Err(err); + } + let mut warnings = Vec::new(); + + let pieces = pieces + .into_iter() + .map(|piece| match piece { + RpfPiece::Lit(lit) => Piece::Lit(Symbol::intern(lit)), + RpfPiece::NextArgument(arg) => { + warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal); + let arg = parse_arg(&arg, mode, &mut warnings, span, parser.is_source_literal); + Piece::Arg(arg) + } + }) + .collect(); + + Ok((FormatString { input, pieces, span }, warnings)) +} + +fn parse_arg( + arg: &Argument<'_>, + mode: Mode, + warnings: &mut Vec, + input_span: Span, + is_source_literal: bool, +) -> FormatArg { + let span = slice_span(input_span, arg.position_span.clone(), is_source_literal); + + match arg.position { + // Something like "hello {name}" + Position::ArgumentNamed(name) => match (mode, Symbol::intern(name)) { + // Only `#[rustc_on_unimplemented]` can use these + (Mode::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext, + (Mode::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This, + (Mode::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait, + // Any attribute can use these + (_, kw::SelfUpper) => FormatArg::SelfUpper, + (_, generic_param) => FormatArg::GenericParam { generic_param, span }, + }, + + // `{:1}` and `{}` are ignored + Position::ArgumentIs(idx) => { + warnings.push(FormatWarning::PositionalArgument { + span, + help: format!("use `{{{idx}}}` to print a number in braces"), + }); + FormatArg::AsIs(Symbol::intern(&format!("{{{idx}}}"))) + } + Position::ArgumentImplicitlyIs(_) => { + warnings.push(FormatWarning::PositionalArgument { + span, + help: String::from("use `{{}}` to print empty braces"), + }); + FormatArg::AsIs(sym::empty_braces) + } + } +} + +/// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything +/// with specifiers, so emit a warning if they are used. +fn warn_on_format_spec( + spec: &FormatSpec<'_>, + warnings: &mut Vec, + input_span: Span, + is_source_literal: bool, +) { + if spec.ty != "" { + let span = spec + .ty_span + .as_ref() + .map(|inner| slice_span(input_span, inner.clone(), is_source_literal)) + .unwrap_or(input_span); + warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() }) + } +} + +fn slice_span(input: Span, Range { start, end }: Range, is_source_literal: bool) -> Span { + if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input } +} + +pub(crate) fn parse_condition( + input: &MetaItemOrLitParser, +) -> Result { + let span = input.span(); + let pred = parse_predicate(input)?; + Ok(OnUnimplementedCondition { span, pred }) +} + +fn parse_predicate(input: &MetaItemOrLitParser) -> Result { + let Some(meta_item) = input.meta_item() else { + return Err(InvalidOnClause::UnsupportedLiteral { span: input.span() }); + }; + + let Some(predicate) = meta_item.ident() else { + return Err(InvalidOnClause::ExpectedIdentifier { + span: meta_item.path().span(), + path: meta_item.path().get_attribute_path(), + }); + }; + + match meta_item.args() { + ArgParser::List(mis) => match predicate.name { + sym::any => Ok(Predicate::Any(parse_predicate_sequence(mis)?)), + sym::all => Ok(Predicate::All(parse_predicate_sequence(mis)?)), + sym::not => { + if let Some(single) = mis.single() { + Ok(Predicate::Not(Box::new(parse_predicate(single)?))) + } else { + Err(InvalidOnClause::ExpectedOnePredInNot { span: mis.span }) + } + } + invalid_pred => { + Err(InvalidOnClause::InvalidPredicate { span: predicate.span, invalid_pred }) + } + }, + ArgParser::NameValue(p) => { + let Some(value) = p.value_as_ident() else { + return Err(InvalidOnClause::UnsupportedLiteral { span: p.args_span() }); + }; + let name = parse_name(predicate.name); + let value = parse_filter(value.name); + let kv = NameValue { name, value }; + Ok(Predicate::Match(kv)) + } + ArgParser::NoArgs => { + let flag = parse_flag(predicate)?; + Ok(Predicate::Flag(flag)) + } + } +} + +fn parse_predicate_sequence( + sequence: &MetaItemListParser, +) -> Result, InvalidOnClause> { + sequence.mixed().map(parse_predicate).collect() +} + +fn parse_flag(Ident { name, span }: Ident) -> Result { + match name { + sym::crate_local => Ok(Flag::CrateLocal), + sym::direct => Ok(Flag::Direct), + sym::from_desugaring => Ok(Flag::FromDesugaring), + invalid_flag => Err(InvalidOnClause::InvalidFlag { invalid_flag, span }), + } +} + +fn parse_name(name: Symbol) -> Name { + match name { + kw::SelfUpper => Name::SelfUpper, + sym::from_desugaring => Name::FromDesugaring, + sym::cause => Name::Cause, + generic => Name::GenericArg(generic), + } +} + +fn parse_filter(input: Symbol) -> FilterFormatString { + let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic) + .map(|p| match p { + RpfPiece::Lit(s) => LitOrArg::Lit(Symbol::intern(s)), + // We just ignore formatspecs here + RpfPiece::NextArgument(a) => match a.position { + // In `TypeErrCtxt::on_unimplemented_note` we substitute `"{integral}"` even + // if the integer type has been resolved, to allow targeting all integers. + // `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet, + // from the `Display` impl of `InferTy` to be precise. + // + // Don't try to format these later! + Position::ArgumentNamed(arg @ ("integer" | "integral" | "float")) => { + LitOrArg::Lit(Symbol::intern(&format!("{{{arg}}}"))) + } + + Position::ArgumentNamed(arg) => LitOrArg::Arg(Symbol::intern(arg)), + Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(sym::empty_braces), + Position::ArgumentIs(idx) => LitOrArg::Lit(Symbol::intern(&format!("{{{idx}}}"))), + }, + }) + .collect(); + FilterFormatString { pieces } +} + +#[derive(Diagnostic)] +pub(crate) enum InvalidOnClause { + #[diag("empty `on`-clause in `#[rustc_on_unimplemented]`", code = E0232)] + Empty { + #[primary_span] + #[label("empty `on`-clause here")] + span: Span, + }, + #[diag("expected a single predicate in `not(..)`", code = E0232)] + ExpectedOnePredInNot { + #[primary_span] + #[label("unexpected quantity of predicates here")] + span: Span, + }, + #[diag("literals inside `on`-clauses are not supported", code = E0232)] + UnsupportedLiteral { + #[primary_span] + #[label("unexpected literal here")] + span: Span, + }, + #[diag("expected an identifier inside this `on`-clause", code = E0232)] + ExpectedIdentifier { + #[primary_span] + #[label("expected an identifier here, not `{$path}`")] + span: Span, + path: AttrPath, + }, + #[diag("this predicate is invalid", code = E0232)] + InvalidPredicate { + #[primary_span] + #[label("expected one of `any`, `all` or `not` here, not `{$invalid_pred}`")] + span: Span, + invalid_pred: Symbol, + }, + #[diag("invalid flag in `on`-clause", code = E0232)] + InvalidFlag { + #[primary_span] + #[label( + "expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}`" + )] + span: Span, + invalid_flag: Symbol, + }, +} + +#[derive(Diagnostic)] +#[diag("this attribute must have a value", code = E0232)] +#[note("e.g. `#[rustc_on_unimplemented(message=\"foo\")]`")] +pub(crate) struct NoValueInOnUnimplemented { + #[primary_span] + #[label("expected value here")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag( + "using multiple `rustc_on_unimplemented` (or mixing it with `diagnostic::on_unimplemented`) is not supported" +)] +pub(crate) struct DupesNotAllowed; diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs new file mode 100644 index 0000000000000..def4069f6b477 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs @@ -0,0 +1,65 @@ +use rustc_hir::attrs::diagnostic::Directive; +use rustc_hir::lints::AttributeLintKind; +use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES; + +use crate::attributes::diagnostic::*; +use crate::attributes::prelude::*; + +#[derive(Default)] +pub(crate) struct OnConstParser { + span: Option, + directive: Option<(Span, Directive)>, +} + +impl AttributeParser for OnConstParser { + const ATTRIBUTES: AcceptMapping = &[( + &[sym::diagnostic, sym::on_const], + template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), + |this, cx, args| { + if !cx.features().diagnostic_on_const() { + return; + } + + let span = cx.attr_span; + this.span = Some(span); + + let items = match args { + ArgParser::List(items) if items.len() != 0 => items, + ArgParser::NoArgs | ArgParser::List(_) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MissingOptionsForOnConst, + span, + ); + return; + } + ArgParser::NameValue(_) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnConstAttr { span }, + span, + ); + return; + } + }; + + let Some(directive) = + parse_directive_items(cx, Mode::DiagnosticOnConst, items.mixed(), true) + else { + return; + }; + merge_directives(cx, &mut this.directive, (span, directive)); + }, + )]; + + //FIXME Still checked in `check_attr.rs` + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + if let Some(span) = self.span { + Some(AttributeKind::OnConst { span, directive: self.directive.map(|d| Box::new(d.1)) }) + } else { + None + } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs new file mode 100644 index 0000000000000..12028059b7d40 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs @@ -0,0 +1,88 @@ +use rustc_hir::attrs::diagnostic::Directive; +use rustc_hir::lints::AttributeLintKind; +use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES; + +use crate::attributes::diagnostic::*; +use crate::attributes::prelude::*; + +#[derive(Default)] +pub(crate) struct OnUnimplementedParser { + span: Option, + directive: Option<(Span, Directive)>, +} + +impl OnUnimplementedParser { + fn parse<'sess, S: Stage>( + &mut self, + cx: &mut AcceptContext<'_, 'sess, S>, + args: &ArgParser, + mode: Mode, + ) { + let span = cx.attr_span; + self.span = Some(span); + + // If target is not a trait, returning early will make `finalize` emit a + // `AttributeKind::OnUnimplemented {span, directive: None }`, to prevent it being + // accidentally used on non-trait items like trait aliases. + if !matches!(cx.target, Target::Trait) { + // Lint later emitted in check_attr + return; + } + + let items = match args { + ArgParser::List(items) if items.len() != 0 => items, + ArgParser::NoArgs | ArgParser::List(_) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MissingOptionsForOnUnimplemented, + span, + ); + return; + } + ArgParser::NameValue(_) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnUnimplementedAttr { span }, + span, + ); + return; + } + }; + + if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) { + merge_directives(cx, &mut self.directive, (span, directive)); + }; + } +} + +impl AttributeParser for OnUnimplementedParser { + const ATTRIBUTES: AcceptMapping = &[ + ( + &[sym::diagnostic, sym::on_unimplemented], + template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), + |this, cx, args| { + this.parse(cx, args, Mode::DiagnosticOnUnimplemented); + }, + ), + ( + &[sym::rustc_on_unimplemented], + template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), + |this, cx, args| { + this.parse(cx, args, Mode::RustcOnUnimplemented); + }, + ), + ]; + //FIXME attribute is not parsed for non-traits but diagnostics are issued in `check_attr.rs` + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + if let Some(span) = self.span { + Some(AttributeKind::OnUnimplemented { + span, + directive: self.directive.map(|d| Box::new(d.1)), + }) + } else { + None + } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 0d328d5cc6a70..8ee453d7f4649 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -39,7 +39,7 @@ pub(crate) mod confusables; pub(crate) mod crate_level; pub(crate) mod debugger; pub(crate) mod deprecation; -pub(crate) mod do_not_recommend; +pub(crate) mod diagnostic; pub(crate) mod doc; pub(crate) mod dummy; pub(crate) mod inline; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 3e5d9dc8fb6f5..2cb5e6d8f6149 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -26,7 +26,9 @@ use crate::attributes::confusables::*; use crate::attributes::crate_level::*; use crate::attributes::debugger::*; use crate::attributes::deprecation::*; -use crate::attributes::do_not_recommend::*; +use crate::attributes::diagnostic::do_not_recommend::*; +use crate::attributes::diagnostic::on_const::*; +use crate::attributes::diagnostic::on_unimplemented::*; use crate::attributes::doc::*; use crate::attributes::dummy::*; use crate::attributes::inline::*; @@ -147,6 +149,8 @@ attribute_parsers!( DocParser, MacroUseParser, NakedParser, + OnConstParser, + OnUnimplementedParser, RustcCguTestAttributeParser, StabilityParser, UsedParser, @@ -160,6 +164,7 @@ attribute_parsers!( Combine, Combine, Combine, + Combine, Combine, Combine, Combine, diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index fc46102f6a30f..38a11b427c1f6 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -707,8 +707,9 @@ fn print_crate_info( }; let crate_name = passes::get_crate_name(sess, attrs); let lint_store = crate::unerased_lint_store(sess); - let registered_tools = rustc_resolve::registered_tools_ast(sess.dcx(), attrs); let features = rustc_expand::config::features(sess, attrs, crate_name); + let registered_tools = + rustc_resolve::registered_tools_ast(sess.dcx(), attrs, sess, &features); let lint_levels = rustc_lint::LintLevelsBuilder::crate_root( sess, &features, diff --git a/compiler/rustc_error_codes/src/error_codes/E0230.md b/compiler/rustc_error_codes/src/error_codes/E0230.md index c30a7e38e9c48..b6dcf4b977741 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0230.md +++ b/compiler/rustc_error_codes/src/error_codes/E0230.md @@ -1,26 +1,4 @@ -The `#[rustc_on_unimplemented]` attribute lets you specify a custom error -message for when a particular trait isn't implemented on a type placed in a -position that needs that trait. For example, when the following code is -compiled: +#### Note: this error code is no longer emitted by the compiler. -```compile_fail,E0230 -#![feature(rustc_attrs)] -#![allow(internal_features)] - -#[rustc_on_unimplemented = "error on `{Self}` with params `<{A},{B}>`"] // error -trait BadAnnotation {} -``` - -There will be an error about `bool` not implementing `Index`, followed by a -note saying "the type `bool` cannot be indexed by `u8`". - -As you can see, you can specify type parameters in curly braces for -instantiation with the actual types (using the regular format string syntax) in -a given situation. Furthermore, `{Self}` will be instantiated to the type (in -this case, `bool`) that we tried to use. - -This error appears when the curly braces contain an identifier which doesn't -match with any of the type parameters or the string `Self`. This might happen -if you misspelled a type parameter, or if you intended to use literal curly -braces. If it is the latter, escape the curly braces with a second curly brace -of the same type; e.g., a literal `{` is `{{`. +The `#[rustc_on_unimplemented]` attribute used to raise this error for various +misuses of the attribute; these are now warnings. diff --git a/compiler/rustc_error_codes/src/error_codes/E0231.md b/compiler/rustc_error_codes/src/error_codes/E0231.md index b22e3c7082a8a..5c644c044e41f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0231.md +++ b/compiler/rustc_error_codes/src/error_codes/E0231.md @@ -1,24 +1 @@ -The `#[rustc_on_unimplemented]` attribute lets you specify a custom error -message for when a particular trait isn't implemented on a type placed in a -position that needs that trait. For example, when the following code is -compiled: - -```compile_fail,E0231 -#![feature(rustc_attrs)] -#![allow(internal_features)] - -#[rustc_on_unimplemented = "error on `{Self}` with params `<{A},{}>`"] // error! -trait BadAnnotation {} -``` - -there will be an error about `bool` not implementing `Index`, followed by a -note saying "the type `bool` cannot be indexed by `u8`". - -As you can see, you can specify type parameters in curly braces for -instantiation with the actual types (using the regular format string syntax) in -a given situation. Furthermore, `{Self}` will be instantiated to the type (in -this case, `bool`) that we tried to use. - -This error appears when the curly braces do not contain an identifier. Please -add one of the same name as a type parameter. If you intended to use literal -braces, use `{{` and `}}` to escape them. +#### Note: this error code is no longer emitted by the compiler diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index e33d943fa5461..f9cb557d020dd 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -16,6 +16,7 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; pub use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; +use crate::attrs::diagnostic::*; use crate::attrs::pretty_printing::PrintAttribute; use crate::limit::Limit; use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability}; @@ -1082,6 +1083,20 @@ pub enum AttributeKind { /// Represents `#[non_exhaustive]` NonExhaustive(Span), + /// Represents `#[diagnostic::on_const]`. + OnConst { + span: Span, + /// None if the directive was malformed in some way. + directive: Option>, + }, + + /// Represents `#[rustc_on_unimplemented]` and `#[diagnostic::on_unimplemented]`. + OnUnimplemented { + span: Span, + /// None if the directive was malformed in some way. + directive: Option>, + }, + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), @@ -1139,6 +1154,9 @@ pub enum AttributeKind { /// Represents `#[reexport_test_harness_main]` ReexportTestHarnessMain(Symbol), + /// Represents `#[register_tool]` + RegisterTool(ThinVec, Span), + /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). Repr { reprs: ThinVec<(ReprAttr, Span)>, diff --git a/compiler/rustc_hir/src/attrs/diagnostic.rs b/compiler/rustc_hir/src/attrs/diagnostic.rs new file mode 100644 index 0000000000000..7c66b3f844691 --- /dev/null +++ b/compiler/rustc_hir/src/attrs/diagnostic.rs @@ -0,0 +1,476 @@ +//! Contains the data structures used by the diagnostic attribute family. +use std::fmt; +use std::fmt::Debug; + +pub use rustc_ast::attr::data_structures::*; +use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; +use rustc_span::{DesugaringKind, Span, Symbol, kw}; +use thin_vec::ThinVec; +use tracing::{debug, info}; + +use crate::attrs::PrintAttribute; + +#[derive(Clone, Default, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct Directive { + pub is_rustc_attr: bool, + pub condition: Option, + pub subcommands: ThinVec, + pub message: Option<(Span, FormatString)>, + pub label: Option<(Span, FormatString)>, + pub notes: ThinVec, + pub parent_label: Option, + pub append_const_msg: Option, +} + +impl Directive { + /// Visit all the generic arguments used in the attribute, to see whether they are actually a + /// generic of the item. If not then `visit` must issue a diagnostic. + /// + /// We can't check this while parsing the attribute because `rustc_attr_parsing` doesn't have + /// access to the item an attribute is on. Instead we later call this function in `check_attr`. + pub fn visit_params(&self, visit: &mut impl FnMut(Symbol, Span)) { + if let Some(condition) = &self.condition { + condition.visit_params(visit); + } + + for subcommand in &self.subcommands { + subcommand.visit_params(visit); + } + + if let Some((_, message)) = &self.message { + message.visit_params(visit); + } + if let Some((_, label)) = &self.label { + label.visit_params(visit); + } + + for note in &self.notes { + note.visit_params(visit); + } + + if let Some(parent_label) = &self.parent_label { + parent_label.visit_params(visit); + } + } + + pub fn evaluate_directive( + &self, + trait_name: impl Debug, + condition_options: &ConditionOptions, + args: &FormatArgs, + ) -> OnUnimplementedNote { + let mut message = None; + let mut label = None; + let mut notes = Vec::new(); + let mut parent_label = None; + let mut append_const_msg = None; + info!( + "evaluate_directive({:?}, trait_ref={:?}, options={:?}, args ={:?})", + self, trait_name, condition_options, args + ); + + for command in self.subcommands.iter().chain(Some(self)).rev() { + debug!(?command); + if let Some(ref condition) = command.condition + && !condition.matches_predicate(condition_options) + { + debug!("evaluate_directive: skipping {:?} due to condition", command); + continue; + } + debug!("evaluate_directive: {:?} succeeded", command); + if let Some(ref message_) = command.message { + message = Some(message_.clone()); + } + + if let Some(ref label_) = command.label { + label = Some(label_.clone()); + } + + notes.extend(command.notes.clone()); + + if let Some(ref parent_label_) = command.parent_label { + parent_label = Some(parent_label_.clone()); + } + + append_const_msg = command.append_const_msg; + } + + OnUnimplementedNote { + label: label.map(|l| l.1.format(args)), + message: message.map(|m| m.1.format(args)), + notes: notes.into_iter().map(|n| n.format(args)).collect(), + parent_label: parent_label.map(|e_s| e_s.format(args)), + append_const_msg, + } + } +} + +#[derive(Default, Debug)] +pub struct OnUnimplementedNote { + pub message: Option, + pub label: Option, + pub notes: Vec, + pub parent_label: Option, + // If none, should fall back to a generic message + pub append_const_msg: Option, +} + +/// Append a message for `[const] Trait` errors. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[derive(HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum AppendConstMessage { + #[default] + Default, + Custom(Symbol, Span), +} + +/// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces", +/// either as string pieces or dynamic arguments. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct FormatString { + pub input: Symbol, + pub span: Span, + pub pieces: ThinVec, +} +impl FormatString { + 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.as_str()), + + // `A` if we have `trait Trait {}` and `note = "i'm the actual type of {A}"` + Piece::Arg(FormatArg::GenericParam { generic_param, .. }) => { + match args.generic_args.iter().find(|(p, _)| p == generic_param) { + Some((_, val)) => ret.push_str(val.as_str()), + + None => { + // Apparently this was not actually a generic parameter, so lets write + // what the user wrote. + let _ = fmt::write(&mut ret, format_args!("{{{generic_param}}}")); + } + } + } + // `{Self}` + Piece::Arg(FormatArg::SelfUpper) => { + let slf = match args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) { + Some((_, val)) => val.to_string(), + None => "Self".to_string(), + }; + ret.push_str(&slf); + } + + // It's only `rustc_onunimplemented` from here + Piece::Arg(FormatArg::This) => ret.push_str(&args.this), + 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 visit_params(&self, visit: &mut impl FnMut(Symbol, Span)) { + for piece in &self.pieces { + if let Piece::Arg(FormatArg::GenericParam { generic_param, span }) = piece { + visit(*generic_param, *span); + } + } + } +} + +/// 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::Residual> { +/// ... +/// } +/// +/// async fn an_async_function() -> u32 { +/// let x: Option = None; +/// x?; //~ ERROR the `?` operator +/// 22 +/// } +/// ``` +/// it will look like this: +/// +/// ```rust,ignore (just an example) +/// FormatArgs { +/// this: "FromResidual", +/// trait_sugared: "FromResidual>", +/// item_context: "an async function", +/// generic_args: [("Self", "u32"), ("R", "Option")], +/// } +/// ``` +#[derive(Debug)] +pub struct FormatArgs { + pub this: String, + pub trait_sugared: String, + pub item_context: &'static str, + pub generic_args: Vec<(Symbol, String)>, +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum Piece { + Lit(Symbol), + Arg(FormatArg), +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum FormatArg { + // A generic parameter, like `{T}` if we're on the `From` trait. + GenericParam { + generic_param: Symbol, + span: Span, + }, + // `{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(Symbol), +} + +/// Represents the `on` filter in `#[rustc_on_unimplemented]`. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct OnUnimplementedCondition { + pub span: Span, + pub pred: Predicate, +} +impl OnUnimplementedCondition { + pub fn matches_predicate(self: &OnUnimplementedCondition, options: &ConditionOptions) -> bool { + self.pred.eval(&mut |p| match p { + FlagOrNv::Flag(b) => options.has_flag(*b), + FlagOrNv::NameValue(NameValue { name, value }) => { + let value = value.format(&options.generic_args); + options.contains(*name, value) + } + }) + } + + pub fn visit_params(&self, visit: &mut impl FnMut(Symbol, Span)) { + self.pred.visit_params(self.span, visit); + } +} + +/// Predicate(s) in `#[rustc_on_unimplemented]`'s `on` filter. See [`OnUnimplementedCondition`]. +/// +/// It is similar to the predicate in the `cfg` attribute, +/// and may contain nested predicates. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum Predicate { + /// A condition like `on(crate_local)`. + Flag(Flag), + /// A match, like `on(Rhs = "Whatever")`. + Match(NameValue), + /// Negation, like `on(not($pred))`. + Not(Box), + /// True if all predicates are true, like `on(all($a, $b, $c))`. + All(ThinVec), + /// True if any predicate is true, like `on(any($a, $b, $c))`. + Any(ThinVec), +} + +impl Predicate { + pub fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool { + match self { + Predicate::Flag(flag) => eval(FlagOrNv::Flag(flag)), + Predicate::Match(nv) => eval(FlagOrNv::NameValue(nv)), + Predicate::Not(not) => !not.eval(eval), + Predicate::All(preds) => preds.into_iter().all(|pred| pred.eval(eval)), + Predicate::Any(preds) => preds.into_iter().any(|pred| pred.eval(eval)), + } + } + + pub fn visit_params(&self, span: Span, visit: &mut impl FnMut(Symbol, Span)) { + match self { + Predicate::Flag(_) => {} + Predicate::Match(nv) => nv.visit_params(span, visit), + Predicate::Not(not) => not.visit_params(span, visit), + Predicate::All(preds) | Predicate::Any(preds) => { + preds.iter().for_each(|pred| pred.visit_params(span, visit)) + } + } + } +} + +/// Represents a `MetaWord` in an `on`-filter. +#[derive(Clone, Copy, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum Flag { + /// Whether the code causing the trait bound to not be fulfilled + /// is part of the user's crate. + CrateLocal, + /// Whether the obligation is user-specified rather than derived. + Direct, + /// Whether we are in some kind of desugaring like + /// `?` or `try { .. }`. + FromDesugaring, +} + +/// A `MetaNameValueStr` in an `on`-filter. +/// +/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct NameValue { + pub name: Name, + /// Something like `"&str"` or `"alloc::string::String"`, + /// in which case it just contains a single string piece. + /// But if it is something like `"&[{A}]"` then it must be formatted later. + pub value: FilterFormatString, +} + +impl NameValue { + pub fn visit_params(&self, span: Span, visit: &mut impl FnMut(Symbol, Span)) { + if let Name::GenericArg(arg) = self.name { + visit(arg, span); + } + self.value.visit_params(span, visit); + } +} + +/// The valid names of the `on` filter. +#[derive(Clone, Copy, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum Name { + Cause, + FromDesugaring, + SelfUpper, + GenericArg(Symbol), +} + +#[derive(Debug, Clone)] +pub enum FlagOrNv<'p> { + Flag(&'p Flag), + NameValue(&'p NameValue), +} + +/// Represents a value inside an `on` filter. +/// +/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. +/// If it is a simple literal like this then `pieces` will be `[LitOrArg::Lit("value")]`. +/// The `Arg` variant is used when it contains formatting like +/// `#[rustc_on_unimplemented(on(Self = "&[{A}]", message = "hello"))]`. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct FilterFormatString { + pub pieces: ThinVec, +} + +impl FilterFormatString { + fn format(&self, generic_args: &[(Symbol, String)]) -> String { + let mut ret = String::new(); + + for piece in &self.pieces { + match piece { + LitOrArg::Lit(s) => ret.push_str(s.as_str()), + LitOrArg::Arg(s) => match generic_args.iter().find(|(k, _)| k == s) { + Some((_, val)) => ret.push_str(val), + None => { + let _ = std::fmt::write(&mut ret, format_args!("{{{s}}}")); + } + }, + } + } + + ret + } + pub fn visit_params(&self, span: Span, visit: &mut impl FnMut(Symbol, Span)) { + for piece in &self.pieces { + if let LitOrArg::Arg(arg) = piece { + visit(*arg, span); + } + } + } +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum LitOrArg { + Lit(Symbol), + Arg(Symbol), +} + +/// Used with `OnUnimplementedCondition::matches_predicate` to evaluate the +/// [`OnUnimplementedCondition`]. +/// +/// 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::Residual> { +/// ... +/// } +/// +/// async fn an_async_function() -> u32 { +/// let x: Option = 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"), +/// ("R", "core::option::Option" ), +/// ], +/// } +/// ``` +#[derive(Debug)] +pub struct ConditionOptions { + /// All the self types that may apply. + pub self_types: Vec, + // The kind of compiler desugaring. + pub from_desugaring: Option, + /// Match on a variant of rustc_infer's `ObligationCauseCode`. + pub cause: Option, + 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)>, +} + +impl ConditionOptions { + pub fn has_flag(&self, name: Flag) -> bool { + match name { + Flag::CrateLocal => self.crate_local, + Flag::Direct => self.direct, + Flag::FromDesugaring => self.from_desugaring.is_some(), + } + } + pub fn contains(&self, name: Name, value: String) -> bool { + match name { + Name::SelfUpper => self.self_types.contains(&value), + Name::FromDesugaring => self.from_desugaring.is_some_and(|ds| ds.matches(&value)), + Name::Cause => self.cause == Some(value), + Name::GenericArg(arg) => self.generic_args.contains(&(arg, value)), + } + } +} diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 3088d4bc32858..1743eef1d8f2c 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -78,6 +78,8 @@ impl AttributeKind { NoMangle(..) => Yes, // Needed for rustdoc NoStd(..) => No, NonExhaustive(..) => Yes, // Needed for rustdoc + OnConst { .. } => Yes, + OnUnimplemented { .. } => Yes, Optimize(..) => No, PanicRuntime => No, PatchableFunctionEntry { .. } => Yes, @@ -92,6 +94,7 @@ impl AttributeKind { ProfilerRuntime => No, RecursionLimit { .. } => No, ReexportTestHarnessMain(..) => No, + RegisterTool(..) => No, Repr { .. } => No, RustcAbi { .. } => No, RustcAllocator => No, diff --git a/compiler/rustc_hir/src/attrs/mod.rs b/compiler/rustc_hir/src/attrs/mod.rs index 7e3ac666d0f50..09fa144a16041 100644 --- a/compiler/rustc_hir/src/attrs/mod.rs +++ b/compiler/rustc_hir/src/attrs/mod.rs @@ -9,6 +9,7 @@ pub use encode_cross_crate::EncodeCrossCrate; pub use pretty_printing::PrintAttribute; mod data_structures; +pub mod diagnostic; mod encode_cross_crate; mod pretty_printing; diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index eba2d182d2c48..1589a6de220e7 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -1,6 +1,6 @@ use rustc_data_structures::fingerprint::Fingerprint; -pub use rustc_lint_defs::AttributeLintKind; use rustc_lint_defs::LintId; +pub use rustc_lint_defs::{AttributeLintKind, FormatWarning}; use rustc_macros::HashStable_Generic; use rustc_span::Span; diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index e8323a218e7a8..a6a1b95ce4c1a 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -26,7 +26,6 @@ use rustc_session::lint::builtin::UNINHABITED_STATIC; use rustc_span::source_map::Spanned; use rustc_target::spec::{AbiMap, AbiMapping}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; -use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use tracing::{debug, instrument}; @@ -805,7 +804,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().type_of(def_id); tcx.ensure_ok().predicates_of(def_id); tcx.ensure_ok().associated_items(def_id); - check_diagnostic_attrs(tcx, def_id); if of_trait { let impl_trait_header = tcx.impl_trait_header(def_id); res = res.and( @@ -828,7 +826,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().predicates_of(def_id); tcx.ensure_ok().associated_items(def_id); let assoc_items = tcx.associated_items(def_id); - check_diagnostic_attrs(tcx, def_id); for &assoc_item in assoc_items.in_definition_order() { match assoc_item.kind { @@ -1122,11 +1119,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), }) } -pub(super) fn check_diagnostic_attrs(tcx: TyCtxt<'_>, def_id: LocalDefId) { - // an error would be reported if this fails. - let _ = OnUnimplementedDirective::of_item(tcx, def_id.to_def_id()); -} - pub(super) fn check_specialization_validity<'tcx>( tcx: TyCtxt<'tcx>, trait_def: &ty::TraitDef, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 862a51288730d..50bdedf973854 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -16,6 +16,7 @@ use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, MultiSpan, StashKey, listify, pluralize, struct_span_code_err, }; +use rustc_hir::attrs::diagnostic::OnUnimplementedNote; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; @@ -37,7 +38,6 @@ use rustc_span::{ kw, sym, }; use rustc_trait_selection::error_reporting::traits::DefIdOrName; -use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 3da2b1bf4069e..5aee3f382ff3c 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -4,7 +4,7 @@ use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_errors::{ Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion, }; -use rustc_hir::lints::AttributeLintKind; +use rustc_hir::lints::{AttributeLintKind, FormatWarning}; use rustc_middle::middle::stability; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -446,5 +446,32 @@ pub fn decorate_attribute_lint( &AttributeLintKind::ExpectedNoArgs => lints::ExpectedNoArgs.decorate_lint(diag), &AttributeLintKind::ExpectedNameValue => lints::ExpectedNameValue.decorate_lint(diag), + &AttributeLintKind::MalformedOnUnimplementedAttr { span } => { + lints::MalformedOnUnimplementedAttrLint { span }.decorate_lint(diag) + } + &AttributeLintKind::MalformedOnConstAttr { span } => { + lints::MalformedOnConstAttrLint { span }.decorate_lint(diag) + } + AttributeLintKind::MalformedDiagnosticFormat { warning } => match warning { + FormatWarning::PositionalArgument { .. } => { + lints::DisallowedPositionalArgument.decorate_lint(diag) + } + FormatWarning::InvalidSpecifier { .. } => { + lints::InvalidFormatSpecifier.decorate_lint(diag) + } + }, + AttributeLintKind::DiagnosticWrappedParserError { description, label, span } => { + lints::WrappedParserError { description, label, span: *span }.decorate_lint(diag) + } + &AttributeLintKind::IgnoredDiagnosticOption { option_name, first_span, later_span } => { + lints::IgnoredDiagnosticOption { option_name, first_span, later_span } + .decorate_lint(diag) + } + &AttributeLintKind::MissingOptionsForOnUnimplemented => { + lints::MissingOptionsForOnUnimplementedAttr.decorate_lint(diag) + } + &AttributeLintKind::MissingOptionsForOnConst => { + lints::MissingOptionsForOnConstAttr.decorate_lint(diag) + } } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index a5c3a889826c7..cd0d8765dd933 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -78,7 +78,7 @@ mod transmute; mod types; mod unit_bindings; mod unqualified_local_imports; -mod unused; +pub mod unused; mod utils; use async_closures::AsyncClosureUsage; @@ -125,6 +125,7 @@ use transmute::CheckTransmutes; use types::*; use unit_bindings::*; use unqualified_local_imports::*; +use unused::must_use::*; use unused::*; #[rustfmt::skip] diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 5627f34f82e97..285a7b7f52935 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3869,3 +3869,60 @@ pub(crate) struct UnreachableCfgSelectPredicateWildcard { #[label("always matches")] pub wildcard_span: Span, } + +#[derive(LintDiagnostic)] +#[diag("positional format arguments are not allowed here")] +#[help( + "only named format arguments with the name of one of the generic types are allowed in this context" +)] +pub(crate) struct DisallowedPositionalArgument; + +#[derive(LintDiagnostic)] +#[diag("invalid format specifier")] +#[help("no format specifier are supported in this position")] +pub(crate) struct InvalidFormatSpecifier; + +#[derive(LintDiagnostic)] +#[diag("{$description}")] +pub(crate) struct WrappedParserError<'a> { + pub description: &'a str, + #[label("{$label}")] + pub span: Span, + pub label: &'a str, +} + +#[derive(LintDiagnostic)] +#[diag("`{$option_name}` is ignored due to previous definition of `{$option_name}`")] +pub(crate) struct IgnoredDiagnosticOption { + pub option_name: Symbol, + #[label("`{$option_name}` is first declared here")] + pub first_span: Span, + #[label("`{$option_name}` is later redundantly declared here")] + pub later_span: Span, +} + +#[derive(LintDiagnostic)] +#[diag("missing options for `on_unimplemented` attribute")] +#[help("at least one of the `message`, `note` and `label` options are expected")] +pub(crate) struct MissingOptionsForOnUnimplementedAttr; + +#[derive(LintDiagnostic)] +#[diag("missing options for `on_const` attribute")] +#[help("at least one of the `message`, `note` and `label` options are expected")] +pub(crate) struct MissingOptionsForOnConstAttr; + +#[derive(LintDiagnostic)] +#[diag("malformed `on_unimplemented` attribute")] +#[help("only `message`, `note` and `label` are allowed as options")] +pub(crate) struct MalformedOnUnimplementedAttrLint { + #[label("invalid option found here")] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag("malformed `on_const` attribute")] +#[help("only `message`, `note` and `label` are allowed as options")] +pub(crate) struct MalformedOnConstAttrLint { + #[label("invalid option found here")] + pub span: Span, +} diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 4b3a49af08369..1fd0ee754eb46 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1,579 +1,20 @@ -use std::iter; - use rustc_ast::util::{classify, parser}; use rustc_ast::{self as ast, ExprKind, FnRetTy, HasAttrs as _, StmtKind}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{MultiSpan, pluralize}; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem, find_attr}; -use rustc_infer::traits::util::elaborate; -use rustc_middle::ty::{self, Ty, adjustment}; +use rustc_errors::MultiSpan; +use rustc_hir::{self as hir}; +use rustc_middle::ty::{self, adjustment}; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::edition::Edition::Edition2015; -use rustc_span::{BytePos, Span, Symbol, kw, sym}; -use tracing::instrument; +use rustc_span::{BytePos, Span, kw, sym}; use crate::lints::{ PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag, - UnusedAllocationMutDiag, UnusedClosure, UnusedCoroutine, UnusedDef, UnusedDefSuggestion, - UnusedDelim, UnusedDelimSuggestion, UnusedImportBracesDiag, UnusedOp, UnusedOpSuggestion, - UnusedResult, + UnusedAllocationMutDiag, UnusedDelim, UnusedDelimSuggestion, UnusedImportBracesDiag, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Lint, LintContext}; -declare_lint! { - /// The `unused_must_use` lint detects unused result of a type flagged as - /// `#[must_use]`. - /// - /// ### Example - /// - /// ```rust - /// fn returns_result() -> Result<(), ()> { - /// Ok(()) - /// } - /// - /// fn main() { - /// returns_result(); - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// The `#[must_use]` attribute is an indicator that it is a mistake to - /// ignore the value. See [the reference] for more details. - /// - /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - pub UNUSED_MUST_USE, - Warn, - "unused result of a type flagged as `#[must_use]`", - report_in_external_macro -} - -declare_lint! { - /// The `unused_results` lint checks for the unused result of an - /// expression in a statement. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #![deny(unused_results)] - /// fn foo() -> T { panic!() } - /// - /// fn main() { - /// foo::(); - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Ignoring the return value of a function may indicate a mistake. In - /// cases were it is almost certain that the result should be used, it is - /// recommended to annotate the function with the [`must_use` attribute]. - /// Failure to use such a return value will trigger the [`unused_must_use` - /// lint] which is warn-by-default. The `unused_results` lint is - /// essentially the same, but triggers for *all* return values. - /// - /// This lint is "allow" by default because it can be noisy, and may not be - /// an actual problem. For example, calling the `remove` method of a `Vec` - /// or `HashMap` returns the previous value, which you may not care about. - /// Using this lint would require explicitly ignoring or discarding such - /// values. - /// - /// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// [`unused_must_use` lint]: warn-by-default.html#unused-must-use - pub UNUSED_RESULTS, - Allow, - "unused result of an expression in a statement" -} - -declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]); - -impl<'tcx> LateLintPass<'tcx> for UnusedResults { - fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { - let hir::StmtKind::Semi(mut expr) = s.kind else { - return; - }; - - let mut expr_is_from_block = false; - while let hir::ExprKind::Block(blk, ..) = expr.kind - && let hir::Block { expr: Some(e), .. } = blk - { - expr = e; - expr_is_from_block = true; - } - - if let hir::ExprKind::Ret(..) = expr.kind { - return; - } - - if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind - && let ty = cx.typeck_results().expr_ty(await_expr) - && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind() - && cx.tcx.ty_is_opaque_future(ty) - && let async_fn_def_id = cx.tcx.parent(*future_def_id) - && matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn) - // Check that this `impl Future` actually comes from an `async fn` - && cx.tcx.asyncness(async_fn_def_id).is_async() - && check_must_use_def( - cx, - async_fn_def_id, - expr.span, - "output of future returned by ", - "", - expr_is_from_block, - ) - { - // We have a bare `foo().await;` on an opaque type from an async function that was - // annotated with `#[must_use]`. - return; - } - - let ty = cx.typeck_results().expr_ty(expr); - - let must_use_result = is_ty_must_use(cx, ty, expr, expr.span); - let type_lint_emitted_or_suppressed = match must_use_result { - Some(path) => { - emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block); - true - } - None => false, - }; - - let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block); - - if !fn_warned && type_lint_emitted_or_suppressed { - // We don't warn about unused unit or uninhabited types. - // (See https://github.com/rust-lang/rust/issues/43806 for details.) - return; - } - - let must_use_op = match expr.kind { - // Hardcoding operators here seemed more expedient than the - // refactoring that would be needed to look up the `#[must_use]` - // attribute which does exist on the comparison trait methods - hir::ExprKind::Binary(bin_op, ..) => match bin_op.node { - hir::BinOpKind::Eq - | hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Ne - | hir::BinOpKind::Ge - | hir::BinOpKind::Gt => Some("comparison"), - hir::BinOpKind::Add - | hir::BinOpKind::Sub - | hir::BinOpKind::Div - | hir::BinOpKind::Mul - | hir::BinOpKind::Rem => Some("arithmetic operation"), - hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"), - hir::BinOpKind::BitXor - | hir::BinOpKind::BitAnd - | hir::BinOpKind::BitOr - | hir::BinOpKind::Shl - | hir::BinOpKind::Shr => Some("bitwise operation"), - }, - hir::ExprKind::AddrOf(..) => Some("borrow"), - hir::ExprKind::OffsetOf(..) => Some("`offset_of` call"), - hir::ExprKind::Unary(..) => Some("unary operation"), - // The `offset_of` macro wraps its contents inside a `const` block. - hir::ExprKind::ConstBlock(block) => { - let body = cx.tcx.hir_body(block.body); - if let hir::ExprKind::Block(block, _) = body.value.kind - && let Some(expr) = block.expr - && let hir::ExprKind::OffsetOf(..) = expr.kind - { - Some("`offset_of` call") - } else { - None - } - } - _ => None, - }; - - let mut op_warned = false; - - if let Some(must_use_op) = must_use_op { - let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span); - cx.emit_span_lint( - UNUSED_MUST_USE, - expr.span, - UnusedOp { - op: must_use_op, - label: expr.span, - suggestion: if expr_is_from_block { - UnusedOpSuggestion::BlockTailExpr { - before_span: span.shrink_to_lo(), - after_span: span.shrink_to_hi(), - } - } else { - UnusedOpSuggestion::NormalExpr { span: span.shrink_to_lo() } - }, - }, - ); - op_warned = true; - } - - if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) { - cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty }); - } - - fn check_fn_must_use( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - expr_is_from_block: bool, - ) -> bool { - let maybe_def_id = match expr.kind { - hir::ExprKind::Call(callee, _) => { - match callee.kind { - hir::ExprKind::Path(ref qpath) => { - match cx.qpath_res(qpath, callee.hir_id) { - Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id), - // `Res::Local` if it was a closure, for which we - // do not currently support must-use linting - _ => None, - } - } - _ => None, - } - } - hir::ExprKind::MethodCall(..) => { - cx.typeck_results().type_dependent_def_id(expr.hir_id) - } - _ => None, - }; - if let Some(def_id) = maybe_def_id { - check_must_use_def( - cx, - def_id, - expr.span, - "return value of ", - "", - expr_is_from_block, - ) - } else { - false - } - } - - /// A path through a type to a must_use source. Contains useful info for the lint. - #[derive(Debug)] - enum MustUsePath { - /// Suppress must_use checking. - Suppressed, - /// The root of the normal must_use lint with an optional message. - Def(Span, DefId, Option), - Boxed(Box), - Pinned(Box), - Opaque(Box), - TraitObject(Box), - TupleElement(Vec<(usize, Self)>), - Array(Box, u64), - /// The root of the unused_closures lint. - Closure(Span), - /// The root of the unused_coroutines lint. - Coroutine(Span), - } - - #[instrument(skip(cx, expr), level = "debug", ret)] - fn is_ty_must_use<'tcx>( - cx: &LateContext<'tcx>, - ty: Ty<'tcx>, - expr: &hir::Expr<'_>, - span: Span, - ) -> Option { - if ty.is_unit() { - return Some(MustUsePath::Suppressed); - } - let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id(); - let is_uninhabited = - |t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env()); - if is_uninhabited(ty) { - return Some(MustUsePath::Suppressed); - } - - match *ty.kind() { - ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => { - is_ty_must_use(cx, boxed, expr, span) - .map(|inner| MustUsePath::Boxed(Box::new(inner))) - } - ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => { - let pinned_ty = args.type_at(0); - is_ty_must_use(cx, pinned_ty, expr, span) - .map(|inner| MustUsePath::Pinned(Box::new(inner))) - } - // Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`). - ty::Adt(def, args) - if cx.tcx.is_diagnostic_item(sym::Result, def.did()) - && args.type_at(0).is_unit() - && is_uninhabited(args.type_at(1)) => - { - Some(MustUsePath::Suppressed) - } - // Suppress warnings on `ControlFlow` (e.g. `ControlFlow`). - ty::Adt(def, args) - if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) - && args.type_at(1).is_unit() - && is_uninhabited(args.type_at(0)) => - { - Some(MustUsePath::Suppressed) - } - ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), - ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { - elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied()) - // We only care about self bounds for the impl-trait - .filter_only_self() - .find_map(|(pred, _span)| { - // We only look at the `DefId`, so it is safe to skip the binder here. - if let ty::ClauseKind::Trait(ref poly_trait_predicate) = - pred.kind().skip_binder() - { - let def_id = poly_trait_predicate.trait_ref.def_id; - - is_def_must_use(cx, def_id, span) - } else { - None - } - }) - .map(|inner| MustUsePath::Opaque(Box::new(inner))) - } - ty::Dynamic(binders, _) => binders.iter().find_map(|predicate| { - if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() - { - let def_id = trait_ref.def_id; - is_def_must_use(cx, def_id, span) - .map(|inner| MustUsePath::TraitObject(Box::new(inner))) - } else { - None - } - }), - ty::Tuple(tys) => { - let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind { - debug_assert_eq!(elem_exprs.len(), tys.len()); - elem_exprs - } else { - &[] - }; - - // Default to `expr`. - let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr)); - - let nested_must_use = tys - .iter() - .zip(elem_exprs) - .enumerate() - .filter_map(|(i, (ty, expr))| { - is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path)) - }) - .collect::>(); - - if !nested_must_use.is_empty() { - Some(MustUsePath::TupleElement(nested_must_use)) - } else { - None - } - } - ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) { - // If the array is empty we don't lint, to avoid false positives - Some(0) | None => None, - // If the array is definitely non-empty, we can do `#[must_use]` checking. - Some(len) => is_ty_must_use(cx, ty, expr, span) - .map(|inner| MustUsePath::Array(Box::new(inner), len)), - }, - ty::Closure(..) | ty::CoroutineClosure(..) => Some(MustUsePath::Closure(span)), - ty::Coroutine(def_id, ..) => { - // async fn should be treated as "implementor of `Future`" - let must_use = if cx.tcx.coroutine_is_async(def_id) { - let def_id = cx.tcx.lang_items().future_trait()?; - is_def_must_use(cx, def_id, span) - .map(|inner| MustUsePath::Opaque(Box::new(inner))) - } else { - None - }; - must_use.or(Some(MustUsePath::Coroutine(span))) - } - _ => None, - } - } - - fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option { - if let Some(reason) = find_attr!( - cx.tcx, def_id, - MustUse { reason, .. } => reason - ) { - // check for #[must_use = "..."] - Some(MustUsePath::Def(span, def_id, *reason)) - } else { - None - } - } - - // Returns whether further errors should be suppressed because either a lint has been - // emitted or the type should be ignored. - fn check_must_use_def( - cx: &LateContext<'_>, - def_id: DefId, - span: Span, - descr_pre_path: &str, - descr_post_path: &str, - expr_is_from_block: bool, - ) -> bool { - is_def_must_use(cx, def_id, span) - .map(|must_use_path| { - emit_must_use_untranslated( - cx, - &must_use_path, - descr_pre_path, - descr_post_path, - 1, - false, - expr_is_from_block, - ) - }) - .is_some() - } - - #[instrument(skip(cx), level = "debug")] - fn emit_must_use_untranslated( - cx: &LateContext<'_>, - path: &MustUsePath, - descr_pre: &str, - descr_post: &str, - plural_len: usize, - is_inner: bool, - expr_is_from_block: bool, - ) { - let plural_suffix = pluralize!(plural_len); - - match path { - MustUsePath::Suppressed => {} - MustUsePath::Boxed(path) => { - let descr_pre = &format!("{descr_pre}boxed "); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::Pinned(path) => { - let descr_pre = &format!("{descr_pre}pinned "); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::Opaque(path) => { - let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of "); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::TraitObject(path) => { - let descr_post = &format!(" trait object{plural_suffix}{descr_post}"); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::TupleElement(elems) => { - for (index, path) in elems { - let descr_post = &format!(" in tuple element {index}"); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - } - MustUsePath::Array(path, len) => { - let descr_pre = &format!("{descr_pre}array{plural_suffix} of "); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)), - true, - expr_is_from_block, - ); - } - MustUsePath::Closure(span) => { - cx.emit_span_lint( - UNUSED_MUST_USE, - *span, - UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post }, - ); - } - MustUsePath::Coroutine(span) => { - cx.emit_span_lint( - UNUSED_MUST_USE, - *span, - UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post }, - ); - } - MustUsePath::Def(span, def_id, reason) => { - let ancenstor_span = span.find_ancestor_not_from_macro().unwrap_or(*span); - let is_redundant_let_ignore = cx - .sess() - .source_map() - .span_to_prev_source(ancenstor_span) - .ok() - .map(|prev| prev.trim_end().ends_with("let _ =")) - .unwrap_or(false); - let suggestion_span = - if is_redundant_let_ignore { *span } else { ancenstor_span }; - cx.emit_span_lint( - UNUSED_MUST_USE, - ancenstor_span, - UnusedDef { - pre: descr_pre, - post: descr_post, - cx, - def_id: *def_id, - note: *reason, - suggestion: (!is_inner).then_some(if expr_is_from_block { - UnusedDefSuggestion::BlockTailExpr { - before_span: suggestion_span.shrink_to_lo(), - after_span: suggestion_span.shrink_to_hi(), - } - } else { - UnusedDefSuggestion::NormalExpr { - span: suggestion_span.shrink_to_lo(), - } - }), - }, - ); - } - } - } - } -} +pub mod must_use; declare_lint! { /// The `path_statements` lint detects path statements with no effect. diff --git a/compiler/rustc_lint/src/unused/must_use.rs b/compiler/rustc_lint/src/unused/must_use.rs new file mode 100644 index 0000000000000..f2d621c2ad5ed --- /dev/null +++ b/compiler/rustc_lint/src/unused/must_use.rs @@ -0,0 +1,646 @@ +use std::iter; + +use rustc_errors::pluralize; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, LangItem, find_attr}; +use rustc_infer::traits::util::elaborate; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::{Span, Symbol, sym}; +use tracing::instrument; + +use crate::lints::{ + UnusedClosure, UnusedCoroutine, UnusedDef, UnusedDefSuggestion, UnusedOp, UnusedOpSuggestion, + UnusedResult, +}; +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `unused_must_use` lint detects unused result of a type flagged as + /// `#[must_use]`. + /// + /// ### Example + /// + /// ```rust + /// fn returns_result() -> Result<(), ()> { + /// Ok(()) + /// } + /// + /// fn main() { + /// returns_result(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The `#[must_use]` attribute is an indicator that it is a mistake to + /// ignore the value. See [the reference] for more details. + /// + /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + pub UNUSED_MUST_USE, + Warn, + "unused result of a type flagged as `#[must_use]`", + report_in_external_macro +} + +declare_lint! { + /// The `unused_results` lint checks for the unused result of an + /// expression in a statement. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unused_results)] + /// fn foo() -> T { panic!() } + /// + /// fn main() { + /// foo::(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Ignoring the return value of a function may indicate a mistake. In + /// cases were it is almost certain that the result should be used, it is + /// recommended to annotate the function with the [`must_use` attribute]. + /// Failure to use such a return value will trigger the [`unused_must_use` + /// lint] which is warn-by-default. The `unused_results` lint is + /// essentially the same, but triggers for *all* return values. + /// + /// This lint is "allow" by default because it can be noisy, and may not be + /// an actual problem. For example, calling the `remove` method of a `Vec` + /// or `HashMap` returns the previous value, which you may not care about. + /// Using this lint would require explicitly ignoring or discarding such + /// values. + /// + /// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + /// [`unused_must_use` lint]: warn-by-default.html#unused-must-use + pub UNUSED_RESULTS, + Allow, + "unused result of an expression in a statement" +} + +declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]); + +/// Must the type be used? +#[derive(Debug)] +pub enum IsTyMustUse { + /// Yes, `MustUsePath` contains an explanation for why the type must be used. + /// This will result in `unused_must_use` lint. + Yes(MustUsePath), + /// No, an ordinary type that may be ignored. + /// This will result in `unused_results` lint. + No, + /// No, the type is trivial and thus should always be ignored. + /// (this suppresses `unused_results` lint) + Trivial, +} + +impl IsTyMustUse { + fn map(self, f: impl FnOnce(MustUsePath) -> MustUsePath) -> Self { + match self { + Self::Yes(must_use_path) => Self::Yes(f(must_use_path)), + _ => self, + } + } + + fn yes(self) -> Option { + match self { + Self::Yes(must_use_path) => Some(must_use_path), + _ => None, + } + } +} + +/// A path through a type to a `must_use` source. Contains useful info for the lint. +#[derive(Debug)] +pub enum MustUsePath { + /// The root of the normal `must_use` lint with an optional message. + Def(Span, DefId, Option), + Boxed(Box), + Pinned(Box), + Opaque(Box), + TraitObject(Box), + TupleElement(Vec<(usize, Self)>), + /// `Result` + Result(Box), + /// `ControlFlow` + ControlFlow(Box), + Array(Box, u64), + /// The root of the unused_closures lint. + Closure(Span), + /// The root of the unused_coroutines lint. + Coroutine(Span), +} + +/// Returns `Some(path)` if `ty` should be considered as "`must_use`" in the context of `expr` +/// (`expr` is used to get the parent module, which can affect which types are considered uninhabited). +/// +/// If `simplify_uninhabited` is true, this function considers `Result` and +/// `ControlFlow` the same as `T` (we don't set this *yet* in rustc, but expose this +/// so clippy can use this). +// +// FIXME: remove `simplify_uninhabited` once clippy had a release with the new semantics. +#[instrument(skip(cx, expr), level = "debug", ret)] +pub fn is_ty_must_use<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + expr: &hir::Expr<'_>, + span: Span, + simplify_uninhabited: bool, +) -> IsTyMustUse { + if ty.is_unit() { + return IsTyMustUse::Trivial; + } + + let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id(); + let is_uninhabited = + |t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env()); + + match *ty.kind() { + _ if is_uninhabited(ty) => IsTyMustUse::Trivial, + ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => { + is_ty_must_use(cx, boxed, expr, span, simplify_uninhabited) + .map(|inner| MustUsePath::Boxed(Box::new(inner))) + } + ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => { + let pinned_ty = args.type_at(0); + is_ty_must_use(cx, pinned_ty, expr, span, simplify_uninhabited) + .map(|inner| MustUsePath::Pinned(Box::new(inner))) + } + // Consider `Result` (e.g. `Result<(), !>`) equivalent to `T`. + ty::Adt(def, args) + if simplify_uninhabited + && cx.tcx.is_diagnostic_item(sym::Result, def.did()) + && is_uninhabited(args.type_at(1)) => + { + let ok_ty = args.type_at(0); + is_ty_must_use(cx, ok_ty, expr, span, simplify_uninhabited) + .map(|path| MustUsePath::Result(Box::new(path))) + } + // Consider `ControlFlow` (e.g. `ControlFlow`) equivalent to `T`. + ty::Adt(def, args) + if simplify_uninhabited + && cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) + && is_uninhabited(args.type_at(0)) => + { + let continue_ty = args.type_at(1); + is_ty_must_use(cx, continue_ty, expr, span, simplify_uninhabited) + .map(|path| MustUsePath::ControlFlow(Box::new(path))) + } + // Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`). + ty::Adt(def, args) + if cx.tcx.is_diagnostic_item(sym::Result, def.did()) + && args.type_at(0).is_unit() + && is_uninhabited(args.type_at(1)) => + { + IsTyMustUse::Trivial + } + // Suppress warnings on `ControlFlow` (e.g. `ControlFlow`). + ty::Adt(def, args) + if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) + && args.type_at(1).is_unit() + && is_uninhabited(args.type_at(0)) => + { + IsTyMustUse::Trivial + } + ty::Adt(def, _) => { + is_def_must_use(cx, def.did(), span).map_or(IsTyMustUse::No, IsTyMustUse::Yes) + } + ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { + elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied()) + // We only care about self bounds for the impl-trait + .filter_only_self() + .find_map(|(pred, _span)| { + // We only look at the `DefId`, so it is safe to skip the binder here. + if let ty::ClauseKind::Trait(ref poly_trait_predicate) = + pred.kind().skip_binder() + { + let def_id = poly_trait_predicate.trait_ref.def_id; + + is_def_must_use(cx, def_id, span) + } else { + None + } + }) + .map(|inner| MustUsePath::Opaque(Box::new(inner))) + .map_or(IsTyMustUse::No, IsTyMustUse::Yes) + } + ty::Dynamic(binders, _) => binders + .iter() + .find_map(|predicate| { + if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { + let def_id = trait_ref.def_id; + is_def_must_use(cx, def_id, span) + .map(|inner| MustUsePath::TraitObject(Box::new(inner))) + } else { + None + } + }) + .map_or(IsTyMustUse::No, IsTyMustUse::Yes), + // NB: unit is checked up above; this is only reachable for tuples with at least one element + ty::Tuple(tys) => { + let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind { + debug_assert_eq!(elem_exprs.len(), tys.len()); + elem_exprs + } else { + &[] + }; + + // Default to `expr`. + let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr)); + + let nested_must_use = tys + .iter() + .zip(elem_exprs) + .enumerate() + .filter_map(|(i, (ty, expr))| { + is_ty_must_use(cx, ty, expr, expr.span, simplify_uninhabited) + .yes() + .map(|path| (i, path)) + }) + .collect::>(); + + if !nested_must_use.is_empty() { + IsTyMustUse::Yes(MustUsePath::TupleElement(nested_must_use)) + } else { + IsTyMustUse::No + } + } + ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) { + // If the array is empty we don't lint, to avoid false positives + Some(0) | None => IsTyMustUse::No, + // If the array is definitely non-empty, we can do `#[must_use]` checking. + Some(len) => is_ty_must_use(cx, ty, expr, span, simplify_uninhabited) + .map(|inner| MustUsePath::Array(Box::new(inner), len)), + }, + ty::Closure(..) | ty::CoroutineClosure(..) => IsTyMustUse::Yes(MustUsePath::Closure(span)), + ty::Coroutine(def_id, ..) => { + // async fn should be treated as "implementor of `Future`" + if cx.tcx.coroutine_is_async(def_id) + && let Some(def_id) = cx.tcx.lang_items().future_trait() + { + IsTyMustUse::Yes(MustUsePath::Opaque(Box::new( + is_def_must_use(cx, def_id, span) + .expect("future trait is marked as `#[must_use]`"), + ))) + } else { + IsTyMustUse::Yes(MustUsePath::Coroutine(span)) + } + } + _ => IsTyMustUse::No, + } +} + +impl<'tcx> LateLintPass<'tcx> for UnusedResults { + fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { + let hir::StmtKind::Semi(mut expr) = s.kind else { + return; + }; + + let mut expr_is_from_block = false; + while let hir::ExprKind::Block(blk, ..) = expr.kind + && let hir::Block { expr: Some(e), .. } = blk + { + expr = e; + expr_is_from_block = true; + } + + if let hir::ExprKind::Ret(..) = expr.kind { + return; + } + + if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind + && let ty = cx.typeck_results().expr_ty(await_expr) + && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind() + && cx.tcx.ty_is_opaque_future(ty) + && let async_fn_def_id = cx.tcx.parent(*future_def_id) + && matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn) + // Check that this `impl Future` actually comes from an `async fn` + && cx.tcx.asyncness(async_fn_def_id).is_async() + && check_must_use_def( + cx, + async_fn_def_id, + expr.span, + "output of future returned by ", + "", + expr_is_from_block, + ) + { + // We have a bare `foo().await;` on an opaque type from an async function that was + // annotated with `#[must_use]`. + return; + } + + let ty = cx.typeck_results().expr_ty(expr); + + let must_use_result = is_ty_must_use(cx, ty, expr, expr.span, false); + let type_lint_emitted_or_trivial = match must_use_result { + IsTyMustUse::Yes(path) => { + emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block); + true + } + IsTyMustUse::Trivial => true, + IsTyMustUse::No => false, + }; + + let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block); + + if !fn_warned && type_lint_emitted_or_trivial { + // We don't warn about unused unit or uninhabited types. + // (See https://github.com/rust-lang/rust/issues/43806 for details.) + return; + } + + let must_use_op = match expr.kind { + // Hardcoding operators here seemed more expedient than the + // refactoring that would be needed to look up the `#[must_use]` + // attribute which does exist on the comparison trait methods + hir::ExprKind::Binary(bin_op, ..) => match bin_op.node { + hir::BinOpKind::Eq + | hir::BinOpKind::Lt + | hir::BinOpKind::Le + | hir::BinOpKind::Ne + | hir::BinOpKind::Ge + | hir::BinOpKind::Gt => Some("comparison"), + hir::BinOpKind::Add + | hir::BinOpKind::Sub + | hir::BinOpKind::Div + | hir::BinOpKind::Mul + | hir::BinOpKind::Rem => Some("arithmetic operation"), + hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"), + hir::BinOpKind::BitXor + | hir::BinOpKind::BitAnd + | hir::BinOpKind::BitOr + | hir::BinOpKind::Shl + | hir::BinOpKind::Shr => Some("bitwise operation"), + }, + hir::ExprKind::AddrOf(..) => Some("borrow"), + hir::ExprKind::OffsetOf(..) => Some("`offset_of` call"), + hir::ExprKind::Unary(..) => Some("unary operation"), + // The `offset_of` macro wraps its contents inside a `const` block. + hir::ExprKind::ConstBlock(block) => { + let body = cx.tcx.hir_body(block.body); + if let hir::ExprKind::Block(block, _) = body.value.kind + && let Some(expr) = block.expr + && let hir::ExprKind::OffsetOf(..) = expr.kind + { + Some("`offset_of` call") + } else { + None + } + } + _ => None, + }; + + let op_warned = match must_use_op { + Some(must_use_op) => { + let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span); + cx.emit_span_lint( + UNUSED_MUST_USE, + expr.span, + UnusedOp { + op: must_use_op, + label: expr.span, + suggestion: if expr_is_from_block { + UnusedOpSuggestion::BlockTailExpr { + before_span: span.shrink_to_lo(), + after_span: span.shrink_to_hi(), + } + } else { + UnusedOpSuggestion::NormalExpr { span: span.shrink_to_lo() } + }, + }, + ); + true + } + None => false, + }; + + // Only emit unused results lint if we haven't emitted any of the more specific lints and the expression type is non trivial. + if !(type_lint_emitted_or_trivial || fn_warned || op_warned) { + cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty }); + } + } +} + +/// Checks if `expr` is a \[method\] call expression marked as `#[must_use]` and emits a lint if so. +/// Returns `true` if the lint has been emitted. +fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expr_is_from_block: bool) -> bool { + let maybe_def_id = match expr.kind { + hir::ExprKind::Call(callee, _) => { + if let hir::ExprKind::Path(ref qpath) = callee.kind + // `Res::Local` if it was a closure, for which we + // do not currently support must-use linting + && let Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) = + cx.qpath_res(qpath, callee.hir_id) + { + Some(def_id) + } else { + None + } + } + hir::ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id), + _ => None, + }; + + match maybe_def_id { + Some(def_id) => { + check_must_use_def(cx, def_id, expr.span, "return value of ", "", expr_is_from_block) + } + None => false, + } +} + +fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option { + // check for #[must_use = "..."] + find_attr!(cx.tcx, def_id, MustUse { reason, .. } => reason) + .map(|reason| MustUsePath::Def(span, def_id, *reason)) +} + +/// Returns whether further errors should be suppressed because a lint has been emitted. +fn check_must_use_def( + cx: &LateContext<'_>, + def_id: DefId, + span: Span, + descr_pre_path: &str, + descr_post_path: &str, + expr_is_from_block: bool, +) -> bool { + is_def_must_use(cx, def_id, span) + .map(|must_use_path| { + emit_must_use_untranslated( + cx, + &must_use_path, + descr_pre_path, + descr_post_path, + 1, + false, + expr_is_from_block, + ) + }) + .is_some() +} + +#[instrument(skip(cx), level = "debug")] +fn emit_must_use_untranslated( + cx: &LateContext<'_>, + path: &MustUsePath, + descr_pre: &str, + descr_post: &str, + plural_len: usize, + is_inner: bool, + expr_is_from_block: bool, +) { + let plural_suffix = pluralize!(plural_len); + + match path { + MustUsePath::Boxed(path) => { + let descr_pre = &format!("{descr_pre}boxed "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::Pinned(path) => { + let descr_pre = &format!("{descr_pre}pinned "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::Opaque(path) => { + let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::TraitObject(path) => { + let descr_post = &format!(" trait object{plural_suffix}{descr_post}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::TupleElement(elems) => { + for (index, path) in elems { + let descr_post = &format!(" in tuple element {index}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + } + MustUsePath::Result(path) => { + let descr_post = &format!(" in a `Result` with an uninhabited error{descr_post}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::ControlFlow(path) => { + let descr_post = &format!(" in a `ControlFlow` with an uninhabited break {descr_post}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::Array(path, len) => { + let descr_pre = &format!("{descr_pre}array{plural_suffix} of "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)), + true, + expr_is_from_block, + ); + } + MustUsePath::Closure(span) => { + cx.emit_span_lint( + UNUSED_MUST_USE, + *span, + UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post }, + ); + } + MustUsePath::Coroutine(span) => { + cx.emit_span_lint( + UNUSED_MUST_USE, + *span, + UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post }, + ); + } + MustUsePath::Def(span, def_id, reason) => { + let ancenstor_span = span.find_ancestor_not_from_macro().unwrap_or(*span); + let is_redundant_let_ignore = cx + .sess() + .source_map() + .span_to_prev_source(ancenstor_span) + .ok() + .map(|prev| prev.trim_end().ends_with("let _ =")) + .unwrap_or(false); + let suggestion_span = if is_redundant_let_ignore { *span } else { ancenstor_span }; + cx.emit_span_lint( + UNUSED_MUST_USE, + ancenstor_span, + UnusedDef { + pre: descr_pre, + post: descr_post, + cx, + def_id: *def_id, + note: *reason, + suggestion: (!is_inner).then_some(if expr_is_from_block { + UnusedDefSuggestion::BlockTailExpr { + before_span: suggestion_span.shrink_to_lo(), + after_span: suggestion_span.shrink_to_hi(), + } + } else { + UnusedDefSuggestion::NormalExpr { span: suggestion_span.shrink_to_lo() } + }), + }, + ); + } + } +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index a1b8b135819a0..1492df50a418a 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -834,6 +834,33 @@ pub enum AttributeLintKind { MalformedDoc, ExpectedNoArgs, ExpectedNameValue, + MalformedOnUnimplementedAttr { + span: Span, + }, + MalformedOnConstAttr { + span: Span, + }, + MalformedDiagnosticFormat { + warning: FormatWarning, + }, + DiagnosticWrappedParserError { + description: String, + label: String, + span: Span, + }, + IgnoredDiagnosticOption { + option_name: Symbol, + first_span: Span, + later_span: Span, + }, + MissingOptionsForOnUnimplemented, + MissingOptionsForOnConst, +} + +#[derive(Debug, Clone, HashStable_Generic)] +pub enum FormatWarning { + PositionalArgument { span: Span, help: String }, + InvalidSpecifier { name: String, span: Span }, } pub type RegisteredTools = FxIndexSet; diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 346604a46ef7d..ebfcb50e9cde5 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -399,7 +399,6 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { let mut query_stream = quote! {}; let mut helpers = HelperTokenStreams::default(); - let mut feedable_queries = quote! {}; let mut analyzer_stream = quote! {}; let mut errors = quote! {}; @@ -480,10 +479,6 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { feedable.span(), "Query {name} cannot be both `feedable` and `eval_always`." ); - feedable_queries.extend(quote! { - [#modifiers_stream] - fn #name(#key_ty) #return_ty, - }); } add_to_analyzer_stream(&query, &mut analyzer_stream); @@ -514,11 +509,6 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { } } } - macro_rules! rustc_feedable_queries { - ( $macro:ident! ) => { - $macro!(#feedable_queries); - } - } // Add hints for rust-analyzer mod _analyzer_hints { diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 3a3a2743ec4f8..e303a8aeab917 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -825,7 +825,7 @@ rustc_queries! { /// Returns the explicitly user-written *predicates* of the definition given by `DefId` /// that must be proven true at usage sites (and which can be assumed at definition site). /// - /// You should probably use [`Self::predicates_of`] unless you're looking for + /// You should probably use [`TyCtxt::predicates_of`] unless you're looking for /// predicates with explicit spans for diagnostics purposes. query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { desc { "computing explicit predicates of `{}`", tcx.def_path_str(key) } @@ -2780,4 +2780,3 @@ rustc_queries! { } rustc_with_all_queries! { define_callbacks! } -rustc_feedable_queries! { define_feedable! } diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index 0b575b536cb6e..4e62fbbec77d5 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -1,13 +1,14 @@ //! Helper functions that serve as the immediate implementation of //! `tcx.$query(..)` and its variations. +use rustc_data_structures::assert_matches; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use crate::dep_graph; use crate::dep_graph::{DepKind, DepNodeKey}; use crate::query::erase::{self, Erasable, Erased}; use crate::query::plumbing::QueryVTable; -use crate::query::{QueryCache, QueryMode}; +use crate::query::{EnsureMode, QueryCache, QueryMode}; use crate::ty::TyCtxt; /// Checks whether there is already a value for this key in the in-memory @@ -56,12 +57,12 @@ pub(crate) fn query_ensure<'tcx, Cache>( execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, query_cache: &Cache, key: Cache::Key, - check_cache: bool, + ensure_mode: EnsureMode, ) where Cache: QueryCache, { if try_get_cached(tcx, query_cache, &key).is_none() { - execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }); + execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { ensure_mode }); } } @@ -73,16 +74,20 @@ pub(crate) fn query_ensure_error_guaranteed<'tcx, Cache, T>( execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, query_cache: &Cache, key: Cache::Key, - check_cache: bool, + // This arg is needed to match the signature of `query_ensure`, + // but should always be `EnsureMode::Ok`. + ensure_mode: EnsureMode, ) -> Result<(), ErrorGuaranteed> where Cache: QueryCache>>, Result: Erasable, { + assert_matches!(ensure_mode, EnsureMode::Ok); + if let Some(res) = try_get_cached(tcx, query_cache, &key) { erase::restore_val(res).map(drop) } else { - execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }) + execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { ensure_mode }) .map(erase::restore_val) .map(|res| res.map(drop)) // Either we actually executed the query, which means we got a full `Result`, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 66e4a77ea6a51..bb457ab03fb55 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -6,8 +6,8 @@ pub use self::caches::{ pub use self::job::{QueryInfo, QueryJob, QueryJobId, QueryLatch, QueryWaiter}; pub use self::keys::{AsLocalKey, Key, LocalCrate}; pub use self::plumbing::{ - ActiveKeyStatus, CycleError, CycleErrorHandling, IntoQueryParam, QueryMode, QueryState, - TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk, + ActiveKeyStatus, CycleError, CycleErrorHandling, EnsureMode, IntoQueryParam, QueryMode, + QueryState, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk, }; pub use self::stack::{QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra}; pub use crate::queries::Providers; diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 9652be2551629..2fee3713c4e01 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -97,8 +97,20 @@ impl<'tcx> CycleError> { #[derive(Debug)] pub enum QueryMode { + /// This is a normal query call to `tcx.$query(..)` or `tcx.at(span).$query(..)`. Get, - Ensure { check_cache: bool }, + /// This is a call to `tcx.ensure_ok().$query(..)` or `tcx.ensure_done().$query(..)`. + Ensure { ensure_mode: EnsureMode }, +} + +/// Distinguishes between `tcx.ensure_ok()` and `tcx.ensure_done()` in shared +/// code paths that handle both modes. +#[derive(Debug)] +pub enum EnsureMode { + /// Corresponds to [`TyCtxt::ensure_ok`]. + Ok, + /// Corresponds to [`TyCtxt::ensure_done`]. + Done, } /// Stores function pointers and other metadata for a particular query. @@ -383,6 +395,17 @@ macro_rules! if_return_result_from_ensure_ok { }; } +// Expands to `$item` if the `feedable` modifier is present. +macro_rules! item_if_feedable { + ([] $($item:tt)*) => {}; + ([(feedable) $($rest:tt)*] $($item:tt)*) => { + $($item)* + }; + ([$other:tt $($modifiers:tt)*] $($item:tt)*) => { + item_if_feedable! { [$($modifiers)*] $($item)* } + }; +} + macro_rules! define_callbacks { ( // You might expect the key to be `$K:ty`, but it needs to be `$($K:tt)*` so that @@ -526,7 +549,7 @@ macro_rules! define_callbacks { self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, $crate::query::IntoQueryParam::into_query_param(key), - false, + $crate::query::EnsureMode::Ok, ) } )* @@ -542,7 +565,7 @@ macro_rules! define_callbacks { self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, $crate::query::IntoQueryParam::into_query_param(key), - true, + $crate::query::EnsureMode::Done, ); } )* @@ -577,6 +600,30 @@ macro_rules! define_callbacks { )* } + $( + item_if_feedable! { + [$($modifiers)*] + impl<'tcx, K: $crate::query::IntoQueryParam<$name::Key<'tcx>> + Copy> + TyCtxtFeed<'tcx, K> + { + $(#[$attr])* + #[inline(always)] + pub fn $name(self, value: $name::ProvidedValue<'tcx>) { + let key = self.key().into_query_param(); + let erased_value = $name::provided_to_erased(self.tcx, value); + $crate::query::inner::query_feed( + self.tcx, + dep_graph::DepKind::$name, + &self.tcx.query_system.query_vtables.$name, + &self.tcx.query_system.caches.$name, + key, + erased_value, + ); + } + } + } + )* + /// Holds a `QueryVTable` for each query. /// /// ("Per" just makes this pluralized name more visually distinct.) @@ -666,39 +713,6 @@ macro_rules! define_callbacks { }; } -// Note: `$V` is unused but present so this can be called by `rustc_with_all_queries`. -macro_rules! define_feedable { - ( - $( - $(#[$attr:meta])* - [$($modifiers:tt)*] - fn $name:ident($K:ty) -> $V:ty, - )* - ) => { - $( - impl<'tcx, K: $crate::query::IntoQueryParam<$K> + Copy> TyCtxtFeed<'tcx, K> { - $(#[$attr])* - #[inline(always)] - pub fn $name(self, value: $name::ProvidedValue<'tcx>) { - let key = self.key().into_query_param(); - - let tcx = self.tcx; - let erased_value = $name::provided_to_erased(tcx, value); - - $crate::query::inner::query_feed( - tcx, - dep_graph::DepKind::$name, - &tcx.query_system.query_vtables.$name, - &tcx.query_system.caches.$name, - key, - erased_value, - ); - } - } - )* - } -} - // Each of these queries corresponds to a function pointer field in the // `Providers` struct for requesting a value of that type, and a method // on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index af76ea183c476..b3dc48ea62f7f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -20,6 +20,7 @@ use rustc_feature::{ ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, }; +use rustc_hir::attrs::diagnostic::Directive; use rustc_hir::attrs::{ AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr, MirDialect, MirPhase, ReprAttr, SanitizerSet, @@ -28,9 +29,9 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, HirId, Item, ItemKind, - MethodKind, PartialConstStability, Safety, Stability, StabilityLevel, Target, TraitItem, - find_attr, + self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, GenericParamKind, HirId, + Item, ItemKind, MethodKind, Node, ParamName, PartialConstStability, Safety, Stability, + StabilityLevel, Target, TraitItem, find_attr, }; use rustc_macros::LintDiagnostic; use rustc_middle::hir::nested_filter; @@ -43,8 +44,8 @@ use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; use rustc_session::lint; use rustc_session::lint::builtin::{ - CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES, - UNUSED_ATTRIBUTES, + CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; @@ -231,6 +232,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id,target) }, Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)}, + Attribute::Parsed(AttributeKind::OnUnimplemented{span, directive}) => {self.check_diagnostic_on_unimplemented(*span, hir_id, target,directive.as_deref())}, + Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)} Attribute::Parsed( // tidy-alphabetical-start AttributeKind::RustcAllowIncoherentImpl(..) @@ -289,6 +292,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::ProfilerRuntime | AttributeKind::RecursionLimit { .. } | AttributeKind::ReexportTestHarnessMain(..) + | AttributeKind::RegisterTool(..) // handled below this loop and elsewhere | AttributeKind::Repr { .. } | AttributeKind::RustcAbi { .. } @@ -386,12 +390,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); match attr.path().as_slice() { - [sym::diagnostic, sym::on_unimplemented, ..] => { - self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) - } - [sym::diagnostic, sym::on_const, ..] => { - self.check_diagnostic_on_const(attr.span(), hir_id, target, item) - } [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { self.check_autodiff(hir_id, attr, span, target) } @@ -407,8 +405,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_layout | sym::rustc_autodiff // crate-level attrs, are checked below - | sym::feature - | sym::register_tool, + | sym::feature, .. ] => {} [name, rest@..] => { @@ -608,7 +605,13 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition - fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) { + fn check_diagnostic_on_unimplemented( + &self, + attr_span: Span, + hir_id: HirId, + target: Target, + directive: Option<&Directive>, + ) { if !matches!(target, Target::Trait) { self.tcx.emit_node_span_lint( MISPLACED_DIAGNOSTIC_ATTRIBUTES, @@ -617,6 +620,39 @@ impl<'tcx> CheckAttrVisitor<'tcx> { DiagnosticOnUnimplementedOnlyForTraits, ); } + + if let Some(directive) = directive { + if let Node::Item(Item { + kind: ItemKind::Trait(_, _, _, trait_name, generics, _, _), + .. + }) = self.tcx.hir_node(hir_id) + { + directive.visit_params(&mut |argument_name, span| { + let has_generic = generics.params.iter().any(|p| { + if !matches!(p.kind, GenericParamKind::Lifetime { .. }) + && let ParamName::Plain(name) = p.name + && name.name == argument_name + { + true + } else { + false + } + }); + if !has_generic { + self.tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + hir_id, + span, + errors::UnknownFormatParameterForOnUnimplementedAttr { + argument_name, + trait_name: *trait_name, + help: !directive.is_rustc_attr, + }, + ) + } + }) + } + } } /// Checks if `#[diagnostic::on_const]` is applied to a trait impl @@ -652,6 +688,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr_span, DiagnosticOnConstOnlyForTraitImpls { item_span }, ); + + // We don't check the validity of generic args here...whose generics would that be, anyway? + // The traits' or the impls'? } /// Checks if an `#[inline]` is applied to a function or a closure. diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 281279abd1e1b..4bb8b06f129cc 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -10,7 +10,7 @@ use rustc_hir::Target; use rustc_hir::attrs::{MirDialect, MirPhase}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{MainDefinition, Ty}; -use rustc_span::{DUMMY_SP, Span, Symbol}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol}; use crate::check_attr::ProcMacroKind; use crate::lang_items::Duplicate; @@ -1449,3 +1449,13 @@ pub(crate) struct FunctionNamesDuplicated { #[primary_span] pub spans: Vec, } + +#[derive(LintDiagnostic)] +#[diag("there is no parameter `{$argument_name}` on trait `{$trait_name}`")] +pub(crate) struct UnknownFormatParameterForOnUnimplementedAttr { + pub argument_name: Symbol, + pub trait_name: Ident, + // `false` if we're in rustc_on_unimplemented, since its syntax is a lot more complex. + #[help(r#"expect either a generic argument name or {"`{Self}`"} as format argument"#)] + pub help: bool, +} diff --git a/compiler/rustc_query_impl/src/dep_kind_vtables.rs b/compiler/rustc_query_impl/src/dep_kind_vtables.rs index 882e59cfa1f3a..f892ff05214f9 100644 --- a/compiler/rustc_query_impl/src/dep_kind_vtables.rs +++ b/compiler/rustc_query_impl/src/dep_kind_vtables.rs @@ -2,7 +2,7 @@ use rustc_middle::bug; use rustc_middle::dep_graph::{DepKindVTable, DepNodeKey, KeyFingerprintStyle}; use rustc_middle::query::QueryCache; -use crate::QueryDispatcherUnerased; +use crate::GetQueryVTable; use crate::plumbing::{force_from_dep_node_inner, try_load_from_on_disk_cache_inner}; /// [`DepKindVTable`] constructors for special dep kinds that aren't queries. @@ -102,18 +102,17 @@ mod non_query { /// Shared implementation of the [`DepKindVTable`] constructor for queries. /// Called from macro-generated code for each query. -pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q, Cache>( +pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q>( is_anon: bool, is_eval_always: bool, ) -> DepKindVTable<'tcx> where - Q: QueryDispatcherUnerased<'tcx, Cache>, - Cache: QueryCache + 'tcx, + Q: GetQueryVTable<'tcx>, { let key_fingerprint_style = if is_anon { KeyFingerprintStyle::Opaque } else { - >::key_fingerprint_style() + ::Key::key_fingerprint_style() }; if is_anon || !key_fingerprint_style.reconstructible() { diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 53afcacb63a6c..7cc20fef6c3db 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -8,8 +8,8 @@ use rustc_errors::{Diag, FatalError, StashKey}; use rustc_middle::dep_graph::{DepGraphData, DepNodeKey}; use rustc_middle::query::plumbing::QueryVTable; use rustc_middle::query::{ - ActiveKeyStatus, CycleError, CycleErrorHandling, QueryCache, QueryJob, QueryJobId, QueryLatch, - QueryMode, QueryStackDeferred, QueryStackFrame, QueryState, + ActiveKeyStatus, CycleError, CycleErrorHandling, EnsureMode, QueryCache, QueryJob, QueryJobId, + QueryLatch, QueryMode, QueryStackDeferred, QueryStackFrame, QueryState, }; use rustc_middle::ty::TyCtxt; use rustc_middle::verify_ich::incremental_verify_ich; @@ -276,6 +276,8 @@ fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>( tcx: TyCtxt<'tcx>, span: Span, key: C::Key, + // If present, some previous step has already created a `DepNode` for this + // query+key, which we should reuse instead of creating a new one. dep_node: Option, ) -> (C::Value, Option) { let state = query.query_state(tcx); @@ -582,23 +584,32 @@ fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache>( Some((result, dep_node_index)) } -/// Ensure that either this query has all green inputs or been executed. -/// Executing `query::ensure(D)` is considered a read of the dep-node `D`. -/// Returns true if the query should still run. -/// -/// This function is particularly useful when executing passes for their -/// side-effects -- e.g., in order to report errors for erroneous programs. +/// Return value struct for [`check_if_ensure_can_skip_execution`]. +struct EnsureCanSkip { + /// If true, the current `tcx.ensure_ok()` or `tcx.ensure_done()` query + /// can return early without actually trying to execute. + skip_execution: bool, + /// A dep node that was prepared while checking whether execution can be + /// skipped, to be reused by execution itself if _not_ skipped. + dep_node: Option, +} + +/// Checks whether a `tcx.ensure_ok()` or `tcx.ensure_done()` query call can +/// return early without actually trying to execute. /// -/// Note: The optimization is only available during incr. comp. +/// This only makes sense during incremental compilation, because it relies +/// on having the dependency graph (and in some cases a disk-cached value) +/// from the previous incr-comp session. #[inline(never)] -fn ensure_must_run<'tcx, C: QueryCache>( +fn check_if_ensure_can_skip_execution<'tcx, C: QueryCache>( query: &'tcx QueryVTable<'tcx, C>, tcx: TyCtxt<'tcx>, key: &C::Key, - check_cache: bool, -) -> (bool, Option) { + ensure_mode: EnsureMode, +) -> EnsureCanSkip { + // Queries with `eval_always` should never skip execution. if query.eval_always { - return (true, None); + return EnsureCanSkip { skip_execution: false, dep_node: None }; } // Ensuring an anonymous query makes no sense @@ -615,7 +626,7 @@ fn ensure_must_run<'tcx, C: QueryCache>( // DepNodeIndex. We must invoke the query itself. The performance cost // this introduces should be negligible as we'll immediately hit the // in-memory cache, or another query down the line will. - return (true, Some(dep_node)); + return EnsureCanSkip { skip_execution: false, dep_node: Some(dep_node) }; } Some((serialized_dep_node_index, dep_node_index)) => { dep_graph.read_index(dep_node_index); @@ -624,13 +635,21 @@ fn ensure_must_run<'tcx, C: QueryCache>( } }; - // We do not need the value at all, so do not check the cache. - if !check_cache { - return (false, None); + match ensure_mode { + EnsureMode::Ok => { + // In ensure-ok mode, we can skip execution for this key if the node + // is green. It must have succeeded in the previous session, and + // therefore would succeed in the current session if executed. + EnsureCanSkip { skip_execution: true, dep_node: None } + } + EnsureMode::Done => { + // In ensure-done mode, we can only skip execution for this key if + // there's a disk-cached value available to load later if needed, + // which guarantees the query provider will never run for this key. + let is_loadable = query.is_loadable_from_disk(tcx, key, serialized_dep_node_index); + EnsureCanSkip { skip_execution: is_loadable, dep_node: Some(dep_node) } + } } - - let loadable = query.is_loadable_from_disk(tcx, key, serialized_dep_node_index); - (!loadable, Some(dep_node)) } #[inline(always)] @@ -655,14 +674,20 @@ pub(super) fn get_query_incr<'tcx, C: QueryCache>( ) -> Option { debug_assert!(tcx.dep_graph.is_fully_enabled()); - let dep_node = if let QueryMode::Ensure { check_cache } = mode { - let (must_run, dep_node) = ensure_must_run(query, tcx, &key, check_cache); - if !must_run { - return None; + // Check if query execution can be skipped, for `ensure_ok` or `ensure_done`. + // This might have the side-effect of creating a suitable DepNode, which + // we should reuse for execution instead of creating a new one. + let dep_node: Option = match mode { + QueryMode::Ensure { ensure_mode } => { + let EnsureCanSkip { skip_execution, dep_node } = + check_if_ensure_can_skip_execution(query, tcx, &key, ensure_mode); + if skip_execution { + // Return early to skip execution. + return None; + } + dep_node } - dep_node - } else { - None + QueryMode::Get => None, }; let (result, dep_node_index) = diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 41a947bb4a84c..63cba4bb6172b 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -37,22 +37,18 @@ mod job; mod profiling_support; mod values; -/// Provides access to vtable-like operations for a query (by obtaining a -/// `QueryVTable`), but also keeps track of the "unerased" value type of the -/// query (i.e. the actual result type in the query declaration). +/// Trait that knows how to look up the [`QueryVTable`] for a particular query. /// /// This trait allows some per-query code to be defined in generic functions /// with a trait bound, instead of having to be defined inline within a macro /// expansion. /// /// There is one macro-generated implementation of this trait for each query, -/// on the type `rustc_query_impl::query_impl::$name::QueryType`. -trait QueryDispatcherUnerased<'tcx, C: QueryCache> { - type UnerasedValue; +/// on the type `rustc_query_impl::query_impl::$name::VTableGetter`. +trait GetQueryVTable<'tcx> { + type Cache: QueryCache + 'tcx; - fn query_vtable(tcx: TyCtxt<'tcx>) -> &'tcx QueryVTable<'tcx, C>; - - fn restore_val(value: C::Value) -> Self::UnerasedValue; + fn query_vtable(tcx: TyCtxt<'tcx>) -> &'tcx QueryVTable<'tcx, Self::Cache>; } pub fn query_system<'tcx>( diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 11077e8e0ee20..a25be9bb0156d 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -13,12 +13,13 @@ use rustc_middle::bug; #[expect(unused_imports, reason = "used by doc comments")] use rustc_middle::dep_graph::DepKindVTable; use rustc_middle::dep_graph::{DepKind, DepNode, DepNodeIndex, DepNodeKey, SerializedDepNodeIndex}; +use rustc_middle::query::erase::{Erasable, Erased}; use rustc_middle::query::on_disk_cache::{ AbsoluteBytePos, CacheDecoder, CacheEncoder, EncodedDepNodeIndex, }; use rustc_middle::query::plumbing::QueryVTable; use rustc_middle::query::{ - Key, QueryCache, QueryJobId, QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra, + Key, QueryCache, QueryJobId, QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra, erase, }; use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::print::with_reduced_queries; @@ -27,7 +28,6 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_serialize::{Decodable, Encodable}; use rustc_span::def_id::LOCAL_CRATE; -use crate::QueryDispatcherUnerased; use crate::error::{QueryOverflow, QueryOverflowNote}; use crate::execution::{all_inactive, force_query}; use crate::job::{QueryJobMap, find_dep_kind_root}; @@ -324,14 +324,14 @@ where QueryStackFrame::new(info, kind, def_id, def_id_for_ty_in_cycle) } -pub(crate) fn encode_query_results<'a, 'tcx, Q, C: QueryCache>( - query: &'tcx QueryVTable<'tcx, C>, +pub(crate) fn encode_query_results_inner<'a, 'tcx, C, V>( tcx: TyCtxt<'tcx>, + query: &'tcx QueryVTable<'tcx, C>, encoder: &mut CacheEncoder<'a, 'tcx>, query_result_index: &mut EncodedDepNodeIndex, ) where - Q: QueryDispatcherUnerased<'tcx, C>, - Q::UnerasedValue: Encodable>, + C: QueryCache>, + V: Erasable + Encodable>, { let _timer = tcx.prof.generic_activity_with_arg("encode_query_results_for", query.name); @@ -346,7 +346,7 @@ pub(crate) fn encode_query_results<'a, 'tcx, Q, C: QueryCache>( // Encode the type check tables with the `SerializedDepNodeIndex` // as tag. - encoder.encode_tagged(dep_node, &Q::restore_val(*value)); + encoder.encode_tagged(dep_node, &erase::restore_val::(*value)); } }); } @@ -473,7 +473,6 @@ macro_rules! define_queries { pub(crate) mod query_impl { $(pub(crate) mod $name { use super::super::*; - use std::marker::PhantomData; use ::rustc_middle::query::erase::{self, Erased}; pub(crate) mod get_query_incr { @@ -607,29 +606,16 @@ macro_rules! define_queries { } } - #[derive(Copy, Clone, Default)] - pub(crate) struct QueryType<'tcx> { - data: PhantomData<&'tcx ()> - } + /// Marker type that implements [`GetQueryVTable`] for this query. + pub(crate) enum VTableGetter {} - impl<'tcx> QueryDispatcherUnerased<'tcx, queries::$name::Storage<'tcx>> - for QueryType<'tcx> - { - type UnerasedValue = queries::$name::Value<'tcx>; + impl<'tcx> GetQueryVTable<'tcx> for VTableGetter { + type Cache = rustc_middle::queries::$name::Storage<'tcx>; #[inline(always)] - fn query_vtable(tcx: TyCtxt<'tcx>) - -> &'tcx QueryVTable<'tcx, queries::$name::Storage<'tcx>> - { + fn query_vtable(tcx: TyCtxt<'tcx>) -> &'tcx QueryVTable<'tcx, Self::Cache> { &tcx.query_system.query_vtables.$name } - - #[inline(always)] - fn restore_val(value: as QueryCache>::Value) - -> Self::UnerasedValue - { - erase::restore_val::>(value) - } } /// Internal per-query plumbing for collecting the set of active jobs for this query. @@ -683,12 +669,9 @@ macro_rules! define_queries { encoder: &mut CacheEncoder<'_, 'tcx>, query_result_index: &mut EncodedDepNodeIndex ) { - $crate::plumbing::encode_query_results::< - query_impl::$name::QueryType<'tcx>, - _ - > ( - &tcx.query_system.query_vtables.$name, + $crate::plumbing::encode_query_results_inner( tcx, + &tcx.query_system.query_vtables.$name, encoder, query_result_index, ) @@ -773,8 +756,8 @@ macro_rules! define_queries { $( /// `DepKindVTable` constructor for this query. pub(crate) fn $name<'tcx>() -> DepKindVTable<'tcx> { - use $crate::query_impl::$name::QueryType; - make_dep_kind_vtable_for_query::, _>( + use $crate::query_impl::$name::VTableGetter; + make_dep_kind_vtable_for_query::( is_anon!([$($modifiers)*]), is_eval_always!([$($modifiers)*]), ) diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 1ca5c17856262..45c1dee9bf52b 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1201,15 +1201,6 @@ pub(crate) struct ToolWasAlreadyRegistered { pub(crate) old_ident_span: Span, } -#[derive(Diagnostic)] -#[diag("`{$tool}` only accepts identifiers")] -pub(crate) struct ToolOnlyAcceptsIdentifiers { - #[primary_span] - #[label("not an identifier")] - pub(crate) span: Span, - pub(crate) tool: Symbol, -} - #[derive(Subdiagnostic)] pub(crate) enum DefinedHere { #[label("similarly named {$candidate_descr} `{$candidate}` defined here")] diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 99cd485241546..551d89ee6022a 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -4,8 +4,9 @@ use std::mem; use std::sync::Arc; -use rustc_ast::{self as ast, Crate, NodeId, attr}; +use rustc_ast::{self as ast, Crate, DUMMY_NODE_ID, NodeId}; use rustc_ast_pretty::pprust; +use rustc_attr_parsing::AttributeParser; use rustc_errors::{Applicability, DiagCtxtHandle, StashKey}; use rustc_expand::base::{ Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, @@ -15,12 +16,14 @@ use rustc_expand::compile_declarative_macro; use rustc_expand::expand::{ AstFragment, AstFragmentKind, Invocation, InvocationKind, SupportsMacroExpansion, }; -use rustc_hir::StabilityLevel; -use rustc_hir::attrs::{CfgEntry, StrippedCfgItem}; +use rustc_feature::Features; +use rustc_hir::attrs::{AttributeKind, CfgEntry, StrippedCfgItem}; use rustc_hir::def::{self, DefKind, MacroKinds, Namespace, NonMacroAttrKind}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; +use rustc_hir::{Attribute, StabilityLevel}; use rustc_middle::middle::stability; use rustc_middle::ty::{RegisteredTools, TyCtxt}; +use rustc_session::Session; use rustc_session::lint::builtin::{ LEGACY_DERIVE_HELPERS, OUT_OF_SCOPE_MACRO_CALLS, UNKNOWN_DIAGNOSTIC_ATTRIBUTES, UNUSED_MACRO_RULES, UNUSED_MACROS, @@ -122,35 +125,38 @@ fn fast_print_path(path: &ast::Path) -> Symbol { pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools { let (_, pre_configured_attrs) = &*tcx.crate_for_resolver(()).borrow(); - registered_tools_ast(tcx.dcx(), pre_configured_attrs) + registered_tools_ast(tcx.dcx(), pre_configured_attrs, tcx.sess, tcx.features()) } pub fn registered_tools_ast( dcx: DiagCtxtHandle<'_>, pre_configured_attrs: &[ast::Attribute], + sess: &Session, + features: &Features, ) -> RegisteredTools { let mut registered_tools = RegisteredTools::default(); - for attr in attr::filter_by_name(pre_configured_attrs, sym::register_tool) { - for meta_item_inner in attr.meta_item_list().unwrap_or_default() { - match meta_item_inner.ident() { - Some(ident) => { - if let Some(old_ident) = registered_tools.replace(ident) { - dcx.emit_err(errors::ToolWasAlreadyRegistered { - span: ident.span, - tool: ident, - old_ident_span: old_ident.span, - }); - } - } - None => { - dcx.emit_err(errors::ToolOnlyAcceptsIdentifiers { - span: meta_item_inner.span(), - tool: sym::register_tool, - }); - } + + if let Some(Attribute::Parsed(AttributeKind::RegisterTool(tools, _))) = + AttributeParser::parse_limited( + sess, + pre_configured_attrs, + sym::register_tool, + DUMMY_SP, + DUMMY_NODE_ID, + Some(features), + ) + { + for tool in tools { + if let Some(old_tool) = registered_tools.replace(tool) { + dcx.emit_err(errors::ToolWasAlreadyRegistered { + span: tool.span, + tool, + old_ident_span: old_tool.span, + }); } } } + // We implicitly add `rustfmt`, `clippy`, `diagnostic`, `miri` and `rust_analyzer` to known // tools, but it's not an error to register them explicitly. let predefined_tools = diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f87189b4de42c..3d40b7317459b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -864,6 +864,7 @@ symbols! { // it's clearer that it's intended as a dummy value, and more likely // to be detected if it accidentally does get used. empty: "", + empty_braces: "{}", emscripten_wasm_eh, enable, end, diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index cd61a8f654733..0ba46c6ddd56a 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -15,7 +15,6 @@ rustc_infer = { path = "../rustc_infer" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_next_trait_solver = { path = "../rustc_next_trait_solver" } -rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 983890f6b48db..f4a9b6635c91b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -14,9 +14,10 @@ use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, Level, MultiSpan, StashKey, StringPart, Suggestions, msg, pluralize, struct_span_code_err, }; +use rustc_hir::attrs::diagnostic::{AppendConstMessage, OnUnimplementedNote}; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{self as hir, LangItem, Node}; +use rustc_hir::{self as hir, LangItem, Node, find_attr}; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_infer::traits::ImplSource; use rustc_infer::traits::solve::Goal; @@ -37,14 +38,12 @@ use rustc_span::def_id::CrateNum; use rustc_span::{BytePos, DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym}; use tracing::{debug, instrument}; -use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote}; use super::suggestions::get_explanation_based_on_obligation; use super::{ ArgKind, CandidateSimilarity, FindExprBySpan, GetSafeTransmuteErrorAndReason, ImplCandidate, }; use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::TyCategory; -use crate::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; use crate::error_reporting::traits::report_dyn_incompatibility; use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, CoroClosureNotFn}; use crate::infer::{self, InferCtxt, InferCtxtExt as _}; @@ -392,7 +391,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(s) = label { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! - err.span_label(span, s); + err.span_label(span, s.as_str().to_owned()); if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point @@ -912,11 +911,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { diag.long_ty_path(), ); - if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, impl_did) - { - let note = command.evaluate( - self.tcx, - predicate.skip_binder().trait_ref, + if let Some(command) = find_attr!(self.tcx, impl_did, OnConst {directive, ..} => directive.as_deref()).flatten(){ + let note = command.evaluate_directive( + predicate.skip_binder().trait_ref, &condition_options, &format_args, ); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index c239f0b56c83f..bda0c4fa2c6f6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -2,8 +2,6 @@ pub mod ambiguity; pub mod call_kind; pub mod fulfillment_errors; pub mod on_unimplemented; -pub mod on_unimplemented_condition; -pub mod on_unimplemented_format; mod overflow; pub mod suggestions; 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 0af7b0e3253dd..d08e0fa3521bb 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 @@ -1,82 +1,18 @@ -use std::iter; use std::path::PathBuf; -use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit}; -use rustc_errors::codes::*; -use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{AttrArgs, Attribute}; -use rustc_macros::LintDiagnostic; -use rustc_middle::bug; +use rustc_hir::attrs::diagnostic::{ConditionOptions, FormatArgs, OnUnimplementedNote}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::find_attr; +pub use rustc_hir::lints::FormatWarning; use rustc_middle::ty::print::PrintTraitRefExt; -use rustc_middle::ty::{self, GenericArgsRef, GenericParamDef, GenericParamDefKind, TyCtxt}; -use rustc_session::lint::builtin::{ - MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, -}; -use rustc_span::{Span, Symbol, sym}; -use tracing::{debug, info}; +use rustc_middle::ty::{self, GenericParamDef, GenericParamDefKind}; +use rustc_span::Symbol; use super::{ObligationCauseCode, PredicateObligation}; use crate::error_reporting::TypeErrCtxt; -use crate::error_reporting::traits::on_unimplemented_condition::{ - ConditionOptions, OnUnimplementedCondition, -}; -use crate::error_reporting::traits::on_unimplemented_format::{ - Ctx, FormatArgs, FormatString, FormatWarning, -}; -use crate::errors::{InvalidOnClause, NoValueInOnUnimplemented}; -use crate::infer::InferCtxtExt; impl<'tcx> TypeErrCtxt<'_, 'tcx> { - fn impl_similar_to( - &self, - trait_pred: ty::PolyTraitPredicate<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) -> Option<(DefId, GenericArgsRef<'tcx>)> { - let tcx = self.tcx; - let param_env = obligation.param_env; - self.enter_forall(trait_pred, |trait_pred| { - let trait_self_ty = trait_pred.self_ty(); - - let mut self_match_impls = vec![]; - let mut fuzzy_match_impls = vec![]; - - self.tcx.for_each_relevant_impl(trait_pred.def_id(), trait_self_ty, |def_id| { - let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id); - let impl_trait_ref = tcx.impl_trait_ref(def_id).instantiate(tcx, impl_args); - - let impl_self_ty = impl_trait_ref.self_ty(); - - if self.can_eq(param_env, trait_self_ty, impl_self_ty) { - self_match_impls.push((def_id, impl_args)); - - if iter::zip( - trait_pred.trait_ref.args.types().skip(1), - impl_trait_ref.args.types().skip(1), - ) - .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some()) - { - fuzzy_match_impls.push((def_id, impl_args)); - } - } - }); - - let impl_def_id_and_args = if let [impl_] = self_match_impls[..] { - impl_ - } else if let [impl_] = fuzzy_match_impls[..] { - impl_ - } else { - return None; - }; - - #[allow(deprecated)] - tcx.has_attr(impl_def_id_and_args.0, sym::rustc_on_unimplemented) - .then_some(impl_def_id_and_args) - }) - } - /// Used to set on_unimplemented's `ItemContext` /// to be the enclosing (async) block/function/closure fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str> { @@ -107,10 +43,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let (condition_options, format_args) = self.on_unimplemented_components(trait_pred, obligation, long_ty_path); - if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, trait_pred.def_id()) - { - command.evaluate( - self.tcx, + if let Some(command) = find_attr!(self.tcx, trait_pred.def_id(), OnUnimplemented {directive, ..} => directive.as_deref()).flatten() { + command.evaluate_directive( trait_pred.skip_binder().trait_ref, &condition_options, &format_args, @@ -125,10 +59,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, obligation: &PredicateObligation<'tcx>, long_ty_path: &mut Option, - ) -> (ConditionOptions, FormatArgs<'tcx>) { - let (def_id, args) = self - .impl_similar_to(trait_pred, obligation) - .unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args)); + ) -> (ConditionOptions, FormatArgs) { + let (def_id, args) = (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args); let trait_pred = trait_pred.skip_binder(); let mut self_types = vec![]; @@ -276,7 +208,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { })); let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id); - let trait_sugared = trait_pred.trait_ref.print_trait_sugared(); + let trait_sugared = trait_pred.trait_ref.print_trait_sugared().to_string(); let condition_options = ConditionOptions { self_types, @@ -318,617 +250,3 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (condition_options, format_args) } } - -/// Represents a format string in a on_unimplemented attribute, -/// like the "content" in `#[diagnostic::on_unimplemented(message = "content")]` -#[derive(Clone, Debug)] -pub struct OnUnimplementedFormatString { - /// Symbol of the format string, i.e. `"content"` - symbol: Symbol, - /// The span of the format string, i.e. `"content"` - span: Span, - is_diagnostic_namespace_variant: bool, -} - -#[derive(Debug)] -pub struct OnUnimplementedDirective { - condition: Option, - subcommands: Vec, - message: Option<(Span, OnUnimplementedFormatString)>, - label: Option<(Span, OnUnimplementedFormatString)>, - notes: Vec, - parent_label: Option, - append_const_msg: Option, -} - -/// For the `#[rustc_on_unimplemented]` attribute -#[derive(Default, Debug)] -pub struct OnUnimplementedNote { - pub message: Option, - pub label: Option, - pub notes: Vec, - pub parent_label: Option, - // If none, should fall back to a generic message - pub append_const_msg: Option, -} - -/// Append a message for `[const] Trait` errors. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] -pub enum AppendConstMessage { - #[default] - Default, - Custom(Symbol, Span), -} - -#[derive(LintDiagnostic)] -#[diag("malformed `on_unimplemented` attribute")] -#[help("only `message`, `note` and `label` are allowed as options")] -pub struct MalformedOnUnimplementedAttrLint { - #[label("invalid option found here")] - pub span: Span, -} - -impl MalformedOnUnimplementedAttrLint { - pub fn new(span: Span) -> Self { - Self { span } - } -} - -#[derive(LintDiagnostic)] -#[diag("missing options for `on_unimplemented` attribute")] -#[help("at least one of the `message`, `note` and `label` options are expected")] -pub struct MissingOptionsForOnUnimplementedAttr; - -#[derive(LintDiagnostic)] -#[diag("`{$option_name}` is ignored due to previous definition of `{$option_name}`")] -pub struct IgnoredDiagnosticOption { - pub option_name: &'static str, - #[label("`{$option_name}` is already declared here")] - pub span: Span, - #[label("`{$option_name}` is first declared here")] - pub prev_span: Span, -} - -impl IgnoredDiagnosticOption { - pub fn maybe_emit_warning<'tcx>( - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - new: Option, - old: Option, - option_name: &'static str, - ) { - if let (Some(new_item), Some(old_item)) = (new, old) - && let Some(item_def_id) = item_def_id.as_local() - { - tcx.emit_node_span_lint( - 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("{$description}")] -pub struct WrappedParserError { - pub description: String, - pub label: String, -} - -impl<'tcx> OnUnimplementedDirective { - fn parse( - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - items: &[MetaItemInner], - span: Span, - is_root: bool, - is_diagnostic_namespace_variant: bool, - ) -> Result, ErrorGuaranteed> { - let mut errored = None; - let mut item_iter = items.iter(); - - let parse_value = |value_str, span| { - OnUnimplementedFormatString::try_parse( - tcx, - item_def_id, - value_str, - span, - is_diagnostic_namespace_variant, - ) - .map(Some) - }; - - let condition = if is_root { - None - } else { - let cond = item_iter - .next() - .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClause::Empty { span }))?; - - let generics: Vec = tcx - .generics_of(item_def_id) - .own_params - .iter() - .filter_map(|param| { - if matches!(param.kind, GenericParamDefKind::Lifetime) { - None - } else { - Some(param.name) - } - }) - .collect(); - match OnUnimplementedCondition::parse(cond, &generics) { - Ok(condition) => Some(condition), - Err(e) => return Err(tcx.dcx().emit_err(e)), - } - }; - - let mut message = None; - let mut label = None; - let mut notes = Vec::new(); - let mut parent_label = None; - let mut subcommands = vec![]; - let mut append_const_msg = None; - - let get_value_and_span = |item: &_, key| { - if let MetaItemInner::MetaItem(MetaItem { - path, - kind: MetaItemKind::NameValue(MetaItemLit { span, kind: LitKind::Str(s, _), .. }), - .. - }) = item - && *path == key - { - Some((*s, *span)) - } else { - None - } - }; - - for item in item_iter { - if let Some((message_, span)) = get_value_and_span(item, sym::message) - && message.is_none() - { - message = parse_value(message_, span)?.map(|l| (item.span(), l)); - continue; - } else if let Some((label_, span)) = get_value_and_span(item, sym::label) - && label.is_none() - { - label = parse_value(label_, span)?.map(|l| (item.span(), l)); - continue; - } else if let Some((note_, span)) = get_value_and_span(item, sym::note) { - if let Some(note) = parse_value(note_, span)? { - notes.push(note); - continue; - } - } else if item.has_name(sym::parent_label) - && parent_label.is_none() - && !is_diagnostic_namespace_variant - { - if let Some(parent_label_) = item.value_str() { - parent_label = parse_value(parent_label_, item.span())?; - continue; - } - } else if item.has_name(sym::on) - && is_root - && message.is_none() - && label.is_none() - && notes.is_empty() - && !is_diagnostic_namespace_variant - // FIXME(diagnostic_namespace): disallow filters for now - { - if let Some(items) = item.meta_item_list() { - match Self::parse( - tcx, - item_def_id, - items, - item.span(), - false, - is_diagnostic_namespace_variant, - ) { - Ok(Some(subcommand)) => subcommands.push(subcommand), - Ok(None) => bug!( - "This cannot happen for now as we only reach that if `is_diagnostic_namespace_variant` is false" - ), - Err(reported) => errored = Some(reported), - }; - continue; - } - } else if item.has_name(sym::append_const_msg) - && append_const_msg.is_none() - && !is_diagnostic_namespace_variant - { - if let Some(msg) = item.value_str() { - append_const_msg = Some(AppendConstMessage::Custom(msg, item.span())); - continue; - } else if item.is_word() { - append_const_msg = Some(AppendConstMessage::Default); - continue; - } - } - - if is_diagnostic_namespace_variant { - if let Some(def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(def_id), - vec![item.span()], - MalformedOnUnimplementedAttrLint::new(item.span()), - ); - } - } else { - // nothing found - tcx.dcx().emit_err(NoValueInOnUnimplemented { span: item.span() }); - } - } - - if let Some(reported) = errored { - if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) } - } else { - Ok(Some(OnUnimplementedDirective { - condition, - subcommands, - message, - label, - notes, - parent_label, - append_const_msg, - })) - } - } - - pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result, ErrorGuaranteed> { - let attr = if tcx.is_trait(item_def_id) { - sym::on_unimplemented - } else if let DefKind::Impl { of_trait: true } = tcx.def_kind(item_def_id) { - sym::on_const - } else { - // It could be a trait_alias (`trait MyTrait = SomeOtherTrait`) - // or an implementation (`impl MyTrait for Foo {}`) - // - // We don't support those. - return Ok(None); - }; - if let Some(attr) = { - #[allow(deprecated)] - tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) - } { - return Self::parse_attribute(attr, false, tcx, item_def_id); - } else { - tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, attr]) - .filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose()) - .try_fold(None, |aggr: Option, directive| { - let directive = directive?; - if let Some(aggr) = aggr { - let mut subcommands = aggr.subcommands; - subcommands.extend(directive.subcommands); - let mut notes = aggr.notes; - notes.extend(directive.notes); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.message.as_ref().map(|f| f.0), - aggr.message.as_ref().map(|f| f.0), - "message", - ); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.label.as_ref().map(|f| f.0), - aggr.label.as_ref().map(|f| f.0), - "label", - ); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.condition.as_ref().map(|i| i.span()), - aggr.condition.as_ref().map(|i| i.span()), - "condition", - ); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.parent_label.as_ref().map(|f| f.span), - aggr.parent_label.as_ref().map(|f| f.span), - "parent_label", - ); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.append_const_msg.as_ref().and_then(|c| { - if let AppendConstMessage::Custom(_, s) = c { - Some(*s) - } else { - None - } - }), - aggr.append_const_msg.as_ref().and_then(|c| { - if let AppendConstMessage::Custom(_, s) = c { - Some(*s) - } else { - None - } - }), - "append_const_msg", - ); - - Ok(Some(Self { - condition: aggr.condition.or(directive.condition), - subcommands, - message: aggr.message.or(directive.message), - label: aggr.label.or(directive.label), - notes, - parent_label: aggr.parent_label.or(directive.parent_label), - append_const_msg: aggr.append_const_msg.or(directive.append_const_msg), - })) - } else { - Ok(Some(directive)) - } - }) - } - } - - fn parse_attribute( - attr: &Attribute, - is_diagnostic_namespace_variant: bool, - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - ) -> Result, ErrorGuaranteed> { - let result = if let Some(items) = attr.meta_item_list() { - Self::parse( - tcx, - item_def_id, - &items, - attr.span(), - true, - is_diagnostic_namespace_variant, - ) - } else if let Some(value) = attr.value_str() { - if !is_diagnostic_namespace_variant { - Ok(Some(OnUnimplementedDirective { - condition: None, - message: None, - subcommands: vec![], - label: Some(( - attr.span(), - OnUnimplementedFormatString::try_parse( - tcx, - item_def_id, - value, - attr.value_span().unwrap_or(attr.span()), - is_diagnostic_namespace_variant, - )?, - )), - notes: Vec::new(), - parent_label: None, - append_const_msg: None, - })) - } else { - let item = attr.get_normal_item(); - let report_span = match &item.args { - AttrArgs::Empty => item.path.span, - AttrArgs::Delimited(args) => args.dspan.entire(), - AttrArgs::Eq { eq_span, expr } => eq_span.to(expr.span), - }; - - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - report_span, - MalformedOnUnimplementedAttrLint::new(report_span), - ); - } - Ok(None) - } - } else if is_diagnostic_namespace_variant { - match attr { - Attribute::Unparsed(p) if !matches!(p.args, AttrArgs::Empty) => { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - attr.span(), - MalformedOnUnimplementedAttrLint::new(attr.span()), - ); - } - } - _ => { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - attr.span(), - MissingOptionsForOnUnimplementedAttr, - ) - } - } - }; - - Ok(None) - } else { - let reported = tcx.dcx().delayed_bug("of_item: neither meta_item_list nor value_str"); - return Err(reported); - }; - debug!("of_item({:?}) = {:?}", item_def_id, result); - result - } - - pub(crate) fn evaluate( - &self, - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - condition_options: &ConditionOptions, - args: &FormatArgs<'tcx>, - ) -> OnUnimplementedNote { - let mut message = None; - let mut label = None; - let mut notes = Vec::new(); - let mut parent_label = None; - let mut append_const_msg = None; - info!( - "evaluate({:?}, trait_ref={:?}, options={:?}, args ={:?})", - self, trait_ref, condition_options, args - ); - - for command in self.subcommands.iter().chain(Some(self)).rev() { - debug!(?command); - if let Some(ref condition) = command.condition - && !condition.matches_predicate(condition_options) - { - debug!("evaluate: skipping {:?} due to condition", command); - continue; - } - debug!("evaluate: {:?} succeeded", command); - if let Some(ref message_) = command.message { - message = Some(message_.clone()); - } - - if let Some(ref label_) = command.label { - label = Some(label_.clone()); - } - - notes.extend(command.notes.clone()); - - if let Some(ref parent_label_) = command.parent_label { - parent_label = Some(parent_label_.clone()); - } - - append_const_msg = command.append_const_msg; - } - - OnUnimplementedNote { - label: label.map(|l| l.1.format(tcx, trait_ref, args)), - message: message.map(|m| m.1.format(tcx, trait_ref, args)), - notes: notes.into_iter().map(|n| n.format(tcx, trait_ref, args)).collect(), - parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, args)), - append_const_msg, - } - } -} - -impl<'tcx> OnUnimplementedFormatString { - fn try_parse( - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - from: Symbol, - span: Span, - is_diagnostic_namespace_variant: bool, - ) -> Result { - let result = - OnUnimplementedFormatString { symbol: from, span, is_diagnostic_namespace_variant }; - result.verify(tcx, item_def_id)?; - Ok(result) - } - - fn verify(&self, tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> { - if !tcx.is_trait(trait_def_id) { - return Ok(()); - }; - - let ctx = if self.is_diagnostic_namespace_variant { - Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id } - } else { - Ctx::RustcOnUnimplemented { tcx, trait_def_id } - }; - - let mut result = Ok(()); - - let snippet = tcx.sess.source_map().span_to_snippet(self.span).ok(); - match FormatString::parse(self.symbol, snippet, self.span, &ctx) { - // Warnings about format specifiers, deprecated parameters, wrong parameters etc. - // In other words we'd like to let the author know, but we can still try to format the string later - Ok(FormatString { warnings, .. }) => { - if self.is_diagnostic_namespace_variant { - for w in warnings { - w.emit_warning(tcx, trait_def_id) - } - } else { - for w in warnings { - match w { - FormatWarning::UnknownParam { argument_name, span } => { - let reported = struct_span_code_err!( - tcx.dcx(), - span, - E0230, - "cannot find parameter {} on this trait", - argument_name, - ) - .emit(); - result = Err(reported); - } - FormatWarning::PositionalArgument { span, .. } => { - let reported = struct_span_code_err!( - tcx.dcx(), - span, - E0231, - "positional format arguments are not allowed here" - ) - .emit(); - result = Err(reported); - } - FormatWarning::InvalidSpecifier { .. } - | FormatWarning::FutureIncompat { .. } => {} - } - } - } - } - // Error from the underlying `rustc_parse_format::Parser` - Err(e) => { - // we cannot return errors from processing the format string as hard error here - // as the diagnostic namespace guarantees that malformed input cannot cause an error - // - // if we encounter any error while processing we nevertheless want to show it as warning - // so that users are aware that something is not correct - if self.is_diagnostic_namespace_variant { - if let Some(trait_def_id) = trait_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - tcx.local_def_id_to_hir_id(trait_def_id), - self.span, - WrappedParserError { description: e.description, label: e.label }, - ); - } - } else { - let reported = - struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description) - .emit(); - result = Err(reported); - } - } - } - - result - } - - pub fn format( - &self, - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - args: &FormatArgs<'tcx>, - ) -> String { - let trait_def_id = trait_ref.def_id; - let ctx = if self.is_diagnostic_namespace_variant { - Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id } - } else { - Ctx::RustcOnUnimplemented { tcx, trait_def_id } - }; - - // No point passing a snippet here, we already did that in `verify` - if let Ok(s) = FormatString::parse(self.symbol, None, self.span, &ctx) { - s.format(args) - } else { - // we cannot return errors from processing the format string as hard error here - // as the diagnostic namespace guarantees that malformed input cannot cause an error - // - // if we encounter any error while processing the format string - // we don't want to show the potentially half assembled formatted string, - // therefore we fall back to just showing the input string in this case - // - // The actual parser errors are emitted earlier - // as lint warnings in OnUnimplementedFormatString::verify - self.symbol.as_str().into() - } - } -} 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 deleted file mode 100644 index 171d05230d468..0000000000000 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs +++ /dev/null @@ -1,322 +0,0 @@ -use rustc_ast::{MetaItemInner, MetaItemKind, MetaItemLit}; -use rustc_parse_format::{ParseMode, Parser, Piece, Position}; -use rustc_span::{DesugaringKind, Ident, Span, Symbol, kw, sym}; - -use crate::errors::InvalidOnClause; - -/// Represents the `on` filter in `#[rustc_on_unimplemented]`. -#[derive(Debug)] -pub(crate) struct OnUnimplementedCondition { - span: Span, - pred: Predicate, -} - -impl OnUnimplementedCondition { - pub(crate) fn span(&self) -> Span { - self.span - } - - pub(crate) fn matches_predicate(&self, options: &ConditionOptions) -> bool { - self.pred.eval(&mut |p| match p { - FlagOrNv::Flag(b) => options.has_flag(*b), - FlagOrNv::NameValue(NameValue { name, value }) => { - let value = value.format(&options.generic_args); - options.contains(*name, value) - } - }) - } - - pub(crate) fn parse( - input: &MetaItemInner, - generics: &[Symbol], - ) -> Result { - let span = input.span(); - let pred = Predicate::parse(input, generics)?; - Ok(OnUnimplementedCondition { span, pred }) - } -} - -/// Predicate(s) in `#[rustc_on_unimplemented]`'s `on` filter. See [`OnUnimplementedCondition`]. -/// -/// It is similar to the predicate in the `cfg` attribute, -/// and may contain nested predicates. -#[derive(Debug)] -enum Predicate { - /// A condition like `on(crate_local)`. - Flag(Flag), - /// A match, like `on(Rhs = "Whatever")`. - Match(NameValue), - /// Negation, like `on(not($pred))`. - Not(Box), - /// True if all predicates are true, like `on(all($a, $b, $c))`. - All(Vec), - /// True if any predicate is true, like `on(any($a, $b, $c))`. - Any(Vec), -} - -impl Predicate { - fn parse(input: &MetaItemInner, generics: &[Symbol]) -> Result { - let meta_item = match input { - MetaItemInner::MetaItem(meta_item) => meta_item, - MetaItemInner::Lit(lit) => { - return Err(InvalidOnClause::UnsupportedLiteral { span: lit.span }); - } - }; - - let Some(predicate) = meta_item.ident() else { - return Err(InvalidOnClause::ExpectedIdentifier { - span: meta_item.path.span, - path: meta_item.path.clone(), - }); - }; - - match meta_item.kind { - MetaItemKind::List(ref mis) => match predicate.name { - sym::any => Ok(Predicate::Any(Predicate::parse_sequence(mis, generics)?)), - sym::all => Ok(Predicate::All(Predicate::parse_sequence(mis, generics)?)), - sym::not => match &**mis { - [one] => Ok(Predicate::Not(Box::new(Predicate::parse(one, generics)?))), - [first, .., last] => Err(InvalidOnClause::ExpectedOnePredInNot { - span: first.span().to(last.span()), - }), - [] => Err(InvalidOnClause::ExpectedOnePredInNot { span: meta_item.span }), - }, - invalid_pred => { - Err(InvalidOnClause::InvalidPredicate { span: predicate.span, invalid_pred }) - } - }, - MetaItemKind::NameValue(MetaItemLit { symbol, .. }) => { - let name = Name::parse(predicate, generics)?; - let value = FilterFormatString::parse(symbol); - let kv = NameValue { name, value }; - Ok(Predicate::Match(kv)) - } - MetaItemKind::Word => { - let flag = Flag::parse(predicate)?; - Ok(Predicate::Flag(flag)) - } - } - } - - fn parse_sequence( - sequence: &[MetaItemInner], - generics: &[Symbol], - ) -> Result, InvalidOnClause> { - sequence.iter().map(|item| Predicate::parse(item, generics)).collect() - } - - fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool { - match self { - Predicate::Flag(flag) => eval(FlagOrNv::Flag(flag)), - Predicate::Match(nv) => eval(FlagOrNv::NameValue(nv)), - Predicate::Not(not) => !not.eval(eval), - Predicate::All(preds) => preds.into_iter().all(|pred| pred.eval(eval)), - Predicate::Any(preds) => preds.into_iter().any(|pred| pred.eval(eval)), - } - } -} - -/// Represents a `MetaWord` in an `on`-filter. -#[derive(Debug, Clone, Copy)] -enum Flag { - /// Whether the code causing the trait bound to not be fulfilled - /// is part of the user's crate. - CrateLocal, - /// Whether the obligation is user-specified rather than derived. - Direct, - /// Whether we are in some kind of desugaring like - /// `?` or `try { .. }`. - FromDesugaring, -} - -impl Flag { - fn parse(Ident { name, span }: Ident) -> Result { - match name { - sym::crate_local => Ok(Flag::CrateLocal), - sym::direct => Ok(Flag::Direct), - sym::from_desugaring => Ok(Flag::FromDesugaring), - invalid_flag => Err(InvalidOnClause::InvalidFlag { invalid_flag, span }), - } - } -} - -/// A `MetaNameValueStr` in an `on`-filter. -/// -/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. -#[derive(Debug, Clone)] -struct NameValue { - name: Name, - /// Something like `"&str"` or `"alloc::string::String"`, - /// in which case it just contains a single string piece. - /// But if it is something like `"&[{A}]"` then it must be formatted later. - value: FilterFormatString, -} - -/// The valid names of the `on` filter. -#[derive(Debug, Clone, Copy)] -enum Name { - Cause, - FromDesugaring, - SelfUpper, - GenericArg(Symbol), -} - -impl Name { - fn parse(Ident { name, span }: Ident, generics: &[Symbol]) -> Result { - match name { - kw::SelfUpper => Ok(Name::SelfUpper), - sym::from_desugaring => Ok(Name::FromDesugaring), - sym::cause => Ok(Name::Cause), - generic if generics.contains(&generic) => Ok(Name::GenericArg(generic)), - invalid_name => Err(InvalidOnClause::InvalidName { invalid_name, span }), - } - } -} - -#[derive(Debug, Clone)] -enum FlagOrNv<'p> { - Flag(&'p Flag), - NameValue(&'p NameValue), -} - -/// Represents a value inside an `on` filter. -/// -/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. -/// If it is a simple literal like this then `pieces` will be `[LitOrArg::Lit("value")]`. -/// The `Arg` variant is used when it contains formatting like -/// `#[rustc_on_unimplemented(on(Self = "&[{A}]", message = "hello"))]`. -#[derive(Debug, Clone)] -struct FilterFormatString { - pieces: Vec, -} - -#[derive(Debug, Clone)] -enum LitOrArg { - Lit(String), - Arg(String), -} - -impl FilterFormatString { - fn parse(input: Symbol) -> Self { - let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic) - .map(|p| match p { - Piece::Lit(s) => LitOrArg::Lit(s.to_owned()), - // We just ignore formatspecs here - Piece::NextArgument(a) => match a.position { - // In `TypeErrCtxt::on_unimplemented_note` we substitute `"{integral}"` even - // if the integer type has been resolved, to allow targeting all integers. - // `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet, - // from the `Display` impl of `InferTy` to be precise. - // - // Don't try to format these later! - Position::ArgumentNamed(arg @ "integer" | arg @ "integral" | arg @ "float") => { - LitOrArg::Lit(format!("{{{arg}}}")) - } - - // FIXME(mejrs) We should check if these correspond to a generic of the trait. - Position::ArgumentNamed(arg) => LitOrArg::Arg(arg.to_owned()), - - // FIXME(mejrs) These should really be warnings/errors - Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(String::from("{}")), - Position::ArgumentIs(idx) => LitOrArg::Lit(format!("{{{idx}}}")), - }, - }) - .collect(); - Self { pieces } - } - - fn format(&self, generic_args: &[(Symbol, String)]) -> String { - let mut ret = String::new(); - - for piece in &self.pieces { - match piece { - LitOrArg::Lit(s) => ret.push_str(s), - LitOrArg::Arg(arg) => { - let s = Symbol::intern(arg); - match generic_args.iter().find(|(k, _)| *k == s) { - Some((_, val)) => ret.push_str(val), - None => { - // FIXME(mejrs) If we start checking as mentioned in - // FilterFormatString::parse then this shouldn't happen - let _ = std::fmt::write(&mut ret, format_args!("{{{s}}}")); - } - } - } - } - } - - ret - } -} - -/// Used with `OnUnimplementedCondition::matches_predicate` to evaluate the -/// [`OnUnimplementedCondition`]. -/// -/// 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::Residual> { -/// ... -/// } -/// -/// async fn an_async_function() -> u32 { -/// let x: Option = 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"), -/// ("R", "core::option::Option" ), -/// ], -/// } -/// ``` -#[derive(Debug)] -pub(crate) struct ConditionOptions { - /// All the self types that may apply. - pub(crate) self_types: Vec, - // The kind of compiler desugaring. - pub(crate) from_desugaring: Option, - /// Match on a variant of [rustc_infer::traits::ObligationCauseCode]. - pub(crate) cause: Option, - pub(crate) crate_local: bool, - /// Is the obligation "directly" user-specified, rather than derived? - pub(crate) direct: bool, - // A list of the generic arguments and their reified types. - pub(crate) generic_args: Vec<(Symbol, String)>, -} - -impl ConditionOptions { - fn has_flag(&self, name: Flag) -> bool { - match name { - Flag::CrateLocal => self.crate_local, - Flag::Direct => self.direct, - Flag::FromDesugaring => self.from_desugaring.is_some(), - } - } - fn contains(&self, name: Name, value: String) -> bool { - match name { - Name::SelfUpper => self.self_types.contains(&value), - Name::FromDesugaring => self.from_desugaring.is_some_and(|ds| ds.matches(&value)), - Name::Cause => self.cause == Some(value), - Name::GenericArg(arg) => self.generic_args.contains(&(arg, value)), - } - } -} 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 deleted file mode 100644 index 360a01a8a3759..0000000000000 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs +++ /dev/null @@ -1,336 +0,0 @@ -use std::fmt; -use std::ops::Range; - -use errors::*; -use rustc_middle::ty::print::TraitRefPrintSugared; -use rustc_middle::ty::{GenericParamDefKind, TyCtxt}; -use rustc_parse_format::{ - Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position, -}; -use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_FORMAT_LITERALS; -use rustc_span::def_id::DefId; -use rustc_span::{InnerSpan, Span, Symbol, kw, sym}; - -/// 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, - span: Span, - pieces: Vec, - /// The formatting string was parsed successfully but with warnings - pub warnings: Vec, -} - -#[derive(Debug)] -enum Piece { - Lit(String), - Arg(FormatArg), -} - -#[derive(Debug)] -enum FormatArg { - // A generic parameter, like `{T}` if we're on the `From` trait. - 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), -} - -pub enum Ctx<'tcx> { - // `#[rustc_on_unimplemented]` - RustcOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId }, - // `#[diagnostic::...]` - DiagnosticOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId }, -} - -#[derive(Debug)] -pub enum FormatWarning { - UnknownParam { argument_name: Symbol, span: Span }, - PositionalArgument { span: Span, help: String }, - InvalidSpecifier { name: String, span: Span }, - FutureIncompat { span: Span, help: String }, -} - -impl FormatWarning { - pub fn emit_warning<'tcx>(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) { - match *self { - FormatWarning::UnknownParam { argument_name, span } => { - let this = tcx.item_ident(item_def_id); - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - tcx.local_def_id_to_hir_id(item_def_id), - span, - UnknownFormatParameterForOnUnimplementedAttr { - argument_name, - trait_name: this, - }, - ); - } - } - FormatWarning::PositionalArgument { span, .. } => { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - tcx.local_def_id_to_hir_id(item_def_id), - span, - DisallowedPositionalArgument, - ); - } - } - FormatWarning::InvalidSpecifier { span, .. } => { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - tcx.local_def_id_to_hir_id(item_def_id), - span, - InvalidFormatSpecifier, - ); - } - } - FormatWarning::FutureIncompat { .. } => { - // We've never deprecated anything in diagnostic namespace format strings - // but if we do we will emit a warning here - - // FIXME(mejrs) in a couple releases, start emitting warnings for - // #[rustc_on_unimplemented] deprecated args - } - } - } -} - -/// 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::Residual> { -/// ... -/// } -/// -/// async fn an_async_function() -> u32 { -/// let x: Option = None; -/// x?; //~ ERROR the `?` operator -/// 22 -/// } -/// ``` -/// it will look like this: -/// -/// ```rust,ignore (just an example) -/// FormatArgs { -/// this: "FromResidual", -/// trait_sugared: "FromResidual>", -/// item_context: "an async function", -/// generic_args: [("Self", "u32"), ("R", "Option")], -/// } -/// ``` -#[derive(Debug)] -pub struct FormatArgs<'tcx> { - pub this: String, - pub trait_sugared: TraitRefPrintSugared<'tcx>, - pub item_context: &'static str, - pub generic_args: Vec<(Symbol, String)>, -} - -impl FormatString { - pub fn span(&self) -> Span { - self.span - } - - pub fn parse<'tcx>( - input: Symbol, - snippet: Option, - span: Span, - ctx: &Ctx<'tcx>, - ) -> Result { - let s = input.as_str(); - let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic); - let pieces: Vec<_> = parser.by_ref().collect(); - - if let Some(err) = parser.errors.into_iter().next() { - return Err(err); - } - let mut warnings = Vec::new(); - - let pieces = pieces - .into_iter() - .map(|piece| match piece { - RpfPiece::Lit(lit) => Piece::Lit(lit.into()), - RpfPiece::NextArgument(arg) => { - warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal); - let arg = parse_arg(&arg, ctx, &mut warnings, span, parser.is_source_literal); - Piece::Arg(arg) - } - }) - .collect(); - - Ok(FormatString { input, pieces, span, warnings }) - } - - 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 {}` and `note = "i'm the actual type of {A}"` - 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(), - None => generic_param.to_string(), - }; - ret.push_str(&value); - } - // `{Self}` - Piece::Arg(FormatArg::SelfUpper) => { - let slf = match args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) { - Some((_, val)) => val.to_string(), - None => "Self".to_string(), - }; - ret.push_str(&slf); - } - - // It's only `rustc_onunimplemented` from here - Piece::Arg(FormatArg::This) => ret.push_str(&args.this), - 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<'tcx>( - arg: &Argument<'_>, - ctx: &Ctx<'tcx>, - warnings: &mut Vec, - input_span: Span, - is_source_literal: bool, -) -> FormatArg { - let (Ctx::RustcOnUnimplemented { tcx, trait_def_id } - | Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = *ctx; - - let span = slice_span(input_span, arg.position_span.clone(), is_source_literal); - - match arg.position { - // Something like "hello {name}" - Position::ArgumentNamed(name) => match (ctx, Symbol::intern(name)) { - // Only `#[rustc_on_unimplemented]` can use these - (Ctx::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext, - (Ctx::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This, - (Ctx::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait, - // Any attribute can use these - ( - Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. }, - kw::SelfUpper, - ) => FormatArg::SelfUpper, - ( - Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. }, - generic_param, - ) if tcx.generics_of(trait_def_id).own_params.iter().any(|param| { - !matches!(param.kind, GenericParamDefKind::Lifetime) && param.name == generic_param - }) => - { - FormatArg::GenericParam { generic_param } - } - - (_, argument_name) => { - warnings.push(FormatWarning::UnknownParam { argument_name, span }); - FormatArg::AsIs(format!("{{{}}}", argument_name.as_str())) - } - }, - - // `{:1}` and `{}` are ignored - Position::ArgumentIs(idx) => { - warnings.push(FormatWarning::PositionalArgument { - span, - help: format!("use `{{{idx}}}` to print a number in braces"), - }); - FormatArg::AsIs(format!("{{{idx}}}")) - } - Position::ArgumentImplicitlyIs(_) => { - warnings.push(FormatWarning::PositionalArgument { - span, - help: String::from("use `{{}}` to print empty braces"), - }); - FormatArg::AsIs(String::from("{}")) - } - } -} - -/// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything -/// with specifiers, so emit a warning if they are used. -fn warn_on_format_spec( - spec: &FormatSpec<'_>, - warnings: &mut Vec, - input_span: Span, - is_source_literal: bool, -) { - if spec.ty != "" { - let span = spec - .ty_span - .as_ref() - .map(|inner| slice_span(input_span, inner.clone(), is_source_literal)) - .unwrap_or(input_span); - warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() }) - } -} - -fn slice_span(input: Span, Range { start, end }: Range, is_source_literal: bool) -> Span { - if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input } -} - -pub mod errors { - use rustc_macros::LintDiagnostic; - use rustc_span::Ident; - - use super::*; - - #[derive(LintDiagnostic)] - #[diag("there is no parameter `{$argument_name}` on trait `{$trait_name}`")] - #[help("expect either a generic argument name or {\"`{Self}`\"} as format argument")] - pub struct UnknownFormatParameterForOnUnimplementedAttr { - pub argument_name: Symbol, - pub trait_name: Ident, - } - - #[derive(LintDiagnostic)] - #[diag("positional format arguments are not allowed here")] - #[help( - "only named format arguments with the name of one of the generic types are allowed in this context" - )] - pub struct DisallowedPositionalArgument; - - #[derive(LintDiagnostic)] - #[diag("invalid format specifier")] - #[help("no format specifier are supported in this position")] - pub struct InvalidFormatSpecifier; - - #[derive(LintDiagnostic)] - #[diag("missing options for `on_unimplemented` attribute")] - #[help("at least one of the `message`, `note` and `label` options are expected")] - pub struct MissingOptionsForOnUnimplementedAttr; -} diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 23234c2080690..2bb9816a31005 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,4 +1,3 @@ -use rustc_ast::Path; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ @@ -28,60 +27,6 @@ pub struct UnableToConstructConstantValue<'a> { pub unevaluated: ty::UnevaluatedConst<'a>, } -#[derive(Diagnostic)] -pub enum InvalidOnClause { - #[diag("empty `on`-clause in `#[rustc_on_unimplemented]`", code = E0232)] - Empty { - #[primary_span] - #[label("empty `on`-clause here")] - span: Span, - }, - #[diag("expected a single predicate in `not(..)`", code = E0232)] - ExpectedOnePredInNot { - #[primary_span] - #[label("unexpected quantity of predicates here")] - span: Span, - }, - #[diag("literals inside `on`-clauses are not supported", code = E0232)] - UnsupportedLiteral { - #[primary_span] - #[label("unexpected literal here")] - span: Span, - }, - #[diag("expected an identifier inside this `on`-clause", code = E0232)] - ExpectedIdentifier { - #[primary_span] - #[label("expected an identifier here, not `{$path}`")] - span: Span, - path: Path, - }, - #[diag("this predicate is invalid", code = E0232)] - InvalidPredicate { - #[primary_span] - #[label("expected one of `any`, `all` or `not` here, not `{$invalid_pred}`")] - span: Span, - invalid_pred: Symbol, - }, - #[diag("invalid flag in `on`-clause", code = E0232)] - InvalidFlag { - #[primary_span] - #[label( - "expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}`" - )] - span: Span, - invalid_flag: Symbol, - }, - #[diag("invalid name in `on`-clause", code = E0232)] - InvalidName { - #[primary_span] - #[label( - "expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `{$invalid_name}`" - )] - span: Span, - invalid_name: Symbol, - }, -} - #[derive(Diagnostic)] #[diag("this attribute must have a value", code = E0232)] #[note("e.g. `#[rustc_on_unimplemented(message=\"foo\")]`")] diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index b54522fcc886f..25202ffd67313 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -997,7 +997,8 @@ impl<'b> Pattern for &'b str { #[cfg(any( all(target_arch = "x86_64", target_feature = "sse2"), - all(target_arch = "loongarch64", target_feature = "lsx") + all(target_arch = "loongarch64", target_feature = "lsx"), + all(target_arch = "aarch64", target_feature = "neon") ))] if self.len() <= 32 { if let Some(result) = simd_contains(self, haystack) { @@ -1782,7 +1783,8 @@ impl TwoWayStrategy for RejectAndMatch { /// [0]: http://0x80.pl/articles/simd-strfind.html#sse-avx2 #[cfg(any( all(target_arch = "x86_64", target_feature = "sse2"), - all(target_arch = "loongarch64", target_feature = "lsx") + all(target_arch = "loongarch64", target_feature = "lsx"), + all(target_arch = "aarch64", target_feature = "neon") ))] #[inline] fn simd_contains(needle: &str, haystack: &str) -> Option { @@ -1917,7 +1919,8 @@ fn simd_contains(needle: &str, haystack: &str) -> Option { /// Both slices must have the same length. #[cfg(any( all(target_arch = "x86_64", target_feature = "sse2"), - all(target_arch = "loongarch64", target_feature = "lsx") + all(target_arch = "loongarch64", target_feature = "lsx"), + all(target_arch = "aarch64", target_feature = "neon") ))] #[inline] unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool { diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 6838bb422b0e0..d3f47a01c0ff6 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -2464,7 +2464,7 @@ impl Child { #[cfg_attr(not(test), rustc_diagnostic_item = "process_exit")] pub fn exit(code: i32) -> ! { crate::rt::cleanup(); - crate::sys::os::exit(code) + crate::sys::exit::exit(code) } /// Terminates the process in an abnormal fashion. diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 11c0a0b9daf7b..1e7de695ddae7 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -187,7 +187,7 @@ fn lang_start_internal( cleanup(); // Guard against multiple threads calling `libc::exit` concurrently. // See the documentation for `unique_thread_exit` for more information. - crate::sys::exit_guard::unique_thread_exit(); + crate::sys::exit::unique_thread_exit(); ret_code }) diff --git a/library/std/src/sys/exit_guard.rs b/library/std/src/sys/exit.rs similarity index 60% rename from library/std/src/sys/exit_guard.rs rename to library/std/src/sys/exit.rs index e7d7a478a5baa..53fb92ba077e0 100644 --- a/library/std/src/sys/exit_guard.rs +++ b/library/std/src/sys/exit.rs @@ -19,8 +19,7 @@ cfg_select! { /// * If it is called again on the same thread as the first call, it will abort. /// * If it is called again on a different thread, it will wait in a loop /// (waiting for the process to exit). - #[cfg_attr(any(test, doctest), allow(dead_code))] - pub(crate) fn unique_thread_exit() { + pub fn unique_thread_exit() { use crate::ffi::c_int; use crate::ptr; use crate::sync::atomic::AtomicPtr; @@ -62,9 +61,83 @@ cfg_select! { /// /// Mitigation is ***NOT*** implemented on this platform, either because this platform /// is not affected, or because mitigation is not yet implemented for this platform. - #[cfg_attr(any(test, doctest), allow(dead_code))] - pub(crate) fn unique_thread_exit() { + #[cfg_attr(any(test, doctest), expect(dead_code))] + pub fn unique_thread_exit() { // Mitigation not required on platforms where `exit` is thread-safe. } } } + +pub fn exit(code: i32) -> ! { + cfg_select! { + target_os = "hermit" => { + unsafe { hermit_abi::exit(code) } + } + target_os = "linux" => { + unsafe { + unique_thread_exit(); + libc::exit(code) + } + } + target_os = "motor" => { + moto_rt::process::exit(code) + } + all(target_vendor = "fortanix", target_env = "sgx") => { + crate::sys::pal::abi::exit_with_code(code as _) + } + target_os = "solid_asp3" => { + rtabort!("exit({}) called", code) + } + target_os = "teeos" => { + let _ = code; + panic!("TA should not call `exit`") + } + target_os = "uefi" => { + use r_efi::base::Status; + + use crate::os::uefi::env; + + if let (Some(boot_services), Some(handle)) = + (env::boot_services(), env::try_image_handle()) + { + let boot_services = boot_services.cast::(); + let _ = unsafe { + ((*boot_services.as_ptr()).exit)( + handle.as_ptr(), + Status::from_usize(code as usize), + 0, + crate::ptr::null_mut(), + ) + }; + } + crate::intrinsics::abort() + } + any( + target_family = "unix", + target_os = "wasi", + ) => { + unsafe { libc::exit(code as crate::ffi::c_int) } + } + target_os = "vexos" => { + let _ = code; + + unsafe { + vex_sdk::vexSystemExitRequest(); + + loop { + vex_sdk::vexTasksRun(); + } + } + } + target_os = "windows" => { + unsafe { crate::sys::pal::c::ExitProcess(code as u32) } + } + target_os = "xous" => { + crate::os::xous::ffi::exit(code as u32) + } + _ => { + let _ = code; + crate::intrinsics::abort() + } + } +} diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 5436c144d3330..5ad23972860bb 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -11,7 +11,7 @@ pub mod backtrace; pub mod cmath; pub mod env; pub mod env_consts; -pub mod exit_guard; +pub mod exit; pub mod fd; pub mod fs; pub mod io; diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index 48a7cdcd2f763..05afb41647872 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -57,10 +57,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - unsafe { hermit_abi::exit(code) } -} - pub fn getpid() -> u32 { unsafe { hermit_abi::getpid() as u32 } } diff --git a/library/std/src/sys/pal/motor/os.rs b/library/std/src/sys/pal/motor/os.rs index cdf66e3958dbe..202841a0dbfca 100644 --- a/library/std/src/sys/pal/motor/os.rs +++ b/library/std/src/sys/pal/motor/os.rs @@ -63,10 +63,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - moto_rt::process::exit(code) -} - pub fn getpid() -> u32 { panic!("Pids on Motor OS are u64.") } diff --git a/library/std/src/sys/pal/sgx/abi/mod.rs b/library/std/src/sys/pal/sgx/abi/mod.rs index 1c6c681d4c179..3314f4f3b6223 100644 --- a/library/std/src/sys/pal/sgx/abi/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/mod.rs @@ -96,7 +96,7 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64 } } -pub(super) fn exit_with_code(code: isize) -> ! { +pub fn exit_with_code(code: isize) -> ! { if code != 0 { if let Some(mut out) = panic::SgxPanicOutput::new() { let _ = write!(out, "Exited with status code {code}"); diff --git a/library/std/src/sys/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs index ba47af7ff88d7..dc6352da7c2e6 100644 --- a/library/std/src/sys/pal/sgx/os.rs +++ b/library/std/src/sys/pal/sgx/os.rs @@ -56,10 +56,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - super::abi::exit_with_code(code as _) -} - pub fn getpid() -> u32 { panic!("no pids in SGX") } diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index c336a1042da40..aeb1c7f46e52a 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -63,10 +63,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - rtabort!("exit({}) called", code); -} - pub fn getpid() -> u32 { panic!("no pids on this platform") } diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index a4b1d3c6ae670..72d14ec7fc9df 100644 --- a/library/std/src/sys/pal/teeos/os.rs +++ b/library/std/src/sys/pal/teeos/os.rs @@ -67,10 +67,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(_code: i32) -> ! { - panic!("TA should not call `exit`") -} - pub fn getpid() -> u32 { panic!("no pids on this platform") } diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 5b9785c8371e3..7d54bc9aff131 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -1,13 +1,10 @@ -use r_efi::efi::Status; use r_efi::efi::protocols::{device_path, loaded_image_device_path}; use super::{helpers, unsupported_err}; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; -use crate::os::uefi; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::path::{self, PathBuf}; -use crate::ptr::NonNull; use crate::{fmt, io}; const PATHS_SEP: u16 = b';' as u16; @@ -105,23 +102,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - if let (Some(boot_services), Some(handle)) = - (uefi::env::boot_services(), uefi::env::try_image_handle()) - { - let boot_services: NonNull = boot_services.cast(); - let _ = unsafe { - ((*boot_services.as_ptr()).exit)( - handle.as_ptr(), - Status::from_usize(code as usize), - 0, - crate::ptr::null_mut(), - ) - }; - } - crate::intrinsics::abort() -} - pub fn getpid() -> u32 { panic!("no pids on this platform") } diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index b8280a8f29a02..494d94433db34 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -533,11 +533,6 @@ pub fn home_dir() -> Option { } } -pub fn exit(code: i32) -> ! { - crate::sys::exit_guard::unique_thread_exit(); - unsafe { libc::exit(code as c_int) } -} - pub fn getpid() -> u32 { unsafe { libc::getpid() as u32 } } diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs index cb925ef4348db..99568458184b6 100644 --- a/library/std/src/sys/pal/unsupported/os.rs +++ b/library/std/src/sys/pal/unsupported/os.rs @@ -56,10 +56,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(_code: i32) -> ! { - crate::intrinsics::abort() -} - pub fn getpid() -> u32 { panic!("no pids on this platform") } diff --git a/library/std/src/sys/pal/vexos/mod.rs b/library/std/src/sys/pal/vexos/mod.rs index 16aa3f088f04b..d1380ab8dff14 100644 --- a/library/std/src/sys/pal/vexos/mod.rs +++ b/library/std/src/sys/pal/vexos/mod.rs @@ -1,3 +1,4 @@ +#[path = "../unsupported/os.rs"] pub mod os; #[expect(dead_code)] diff --git a/library/std/src/sys/pal/vexos/os.rs b/library/std/src/sys/pal/vexos/os.rs deleted file mode 100644 index 303b452a078ff..0000000000000 --- a/library/std/src/sys/pal/vexos/os.rs +++ /dev/null @@ -1,19 +0,0 @@ -#[expect(dead_code)] -#[path = "../unsupported/os.rs"] -mod unsupported_os; -pub use unsupported_os::{ - JoinPathsError, SplitPaths, chdir, current_exe, getcwd, getpid, home_dir, join_paths, - split_paths, temp_dir, -}; - -pub use super::unsupported; - -pub fn exit(_code: i32) -> ! { - unsafe { - vex_sdk::vexSystemExitRequest(); - - loop { - vex_sdk::vexTasksRun(); - } - } -} diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index 285be3ca9fda4..4a92e577c6503 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -102,10 +102,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - unsafe { libc::exit(code) } -} - pub fn getpid() -> u32 { panic!("unsupported"); } diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index 3eb6ec8278401..ebbbb128a7c9b 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -189,10 +189,6 @@ pub fn home_dir() -> Option { .or_else(home_dir_crt) } -pub fn exit(code: i32) -> ! { - unsafe { c::ExitProcess(code as u32) } -} - pub fn getpid() -> u32 { unsafe { c::GetCurrentProcessId() } } diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index cd7b7b59d1127..b915bccc7f7d0 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -119,10 +119,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(code: i32) -> ! { - crate::os::xous::ffi::exit(code as u32); -} - pub fn getpid() -> u32 { panic!("no pids on this platform") } diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index cb925ef4348db..99568458184b6 100644 --- a/library/std/src/sys/pal/zkvm/os.rs +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -56,10 +56,6 @@ pub fn home_dir() -> Option { None } -pub fn exit(_code: i32) -> ! { - crate::intrinsics::abort() -} - pub fn getpid() -> u32 { panic!("no pids on this platform") } diff --git a/src/doc/embedded-book b/src/doc/embedded-book index fe88fbb68391a..99d0341ff4e06 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit fe88fbb68391a465680dd91109f0a151a1676f3e +Subproject commit 99d0341ff4e06757490af8fceee790c4ede50bc0 diff --git a/src/doc/reference b/src/doc/reference index addd0602c819b..442cbef910566 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit addd0602c819b6526b9cc97653b0fadca395528c +Subproject commit 442cbef9105662887d5eae2882ca551f3726bf28 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index bac931ef1673a..5383db524711c 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit bac931ef1673af63fb60c3d691633034713cca20 +Subproject commit 5383db524711c0c9c43c3ca9e5e706089672ed6a diff --git a/tests/incremental/issue-59523-on-implemented-is-not-unused.rs b/tests/incremental/issue-59523-on-implemented-is-not-unused.rs index 8260bd350e721..59e972857f377 100644 --- a/tests/incremental/issue-59523-on-implemented-is-not-unused.rs +++ b/tests/incremental/issue-59523-on-implemented-is-not-unused.rs @@ -9,13 +9,12 @@ #![feature(rustc_attrs)] #![deny(unused_attributes)] -#[rustc_on_unimplemented = "invalid"] +#[rustc_on_unimplemented(label ="invalid")] trait Index { type Output: ?Sized; fn index(&self, index: Idx) -> &Self::Output; } -#[rustc_on_unimplemented = "a usize is required to index into a slice"] impl Index for [i32] { type Output = i32; fn index(&self, index: usize) -> &i32 { diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 01e70adf4ee99..004ea7d985aad 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -723,23 +723,6 @@ help: use `#[rustc_align(...)]` instead LL | #[repr] | ^^^^^^^ -warning: missing options for `on_unimplemented` attribute - --> $DIR/malformed-attrs.rs:142:1 - | -LL | #[diagnostic::on_unimplemented] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: at least one of the `message`, `note` and `label` options are expected - = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default - -warning: malformed `on_unimplemented` attribute - --> $DIR/malformed-attrs.rs:144:1 - | -LL | #[diagnostic::on_unimplemented = 1] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` --> $DIR/malformed-attrs.rs:41:1 | @@ -810,6 +793,23 @@ LL | #[no_implicit_prelude = 23] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[no_implicit_prelude]` can be applied to crates and modules +warning: missing options for `on_unimplemented` attribute + --> $DIR/malformed-attrs.rs:142:1 + | +LL | #[diagnostic::on_unimplemented] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least one of the `message`, `note` and `label` options are expected + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: malformed `on_unimplemented` attribute + --> $DIR/malformed-attrs.rs:144:1 + | +LL | #[diagnostic::on_unimplemented = 1] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + warning: `#[diagnostic::do_not_recommend]` does not expect any arguments --> $DIR/malformed-attrs.rs:151:1 | diff --git a/tests/ui/diagnostic_namespace/on_const/auxiliary/const_trait.rs b/tests/ui/diagnostic_namespace/on_const/auxiliary/const_trait.rs new file mode 100644 index 0000000000000..cf854a9072a6a --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/auxiliary/const_trait.rs @@ -0,0 +1,10 @@ +#![feature(diagnostic_on_const)] + +pub struct X; + +#[diagnostic::on_const(message = "message", label = "label", note = "note")] +impl PartialEq for X { + fn eq(&self, _other: &X) -> bool { + true + } +} diff --git a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs index f7babae3aa7c4..48654ed47ec4d 100644 --- a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs +++ b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs @@ -1,4 +1,4 @@ -#![feature(const_trait_impl, const_cmp)] +#![feature(const_trait_impl, const_cmp, diagnostic_on_const)] #![deny(misplaced_diagnostic_attributes)] #[diagnostic::on_const(message = "tadaa", note = "boing")] diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs index 82c2db7e26d7f..56255a25b3f39 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs @@ -1,34 +1,27 @@ //@ reference: attributes.diagnostic.on_unimplemented.invalid-string #[diagnostic::on_unimplemented(message = "{{Test } thing")] //~^WARN unmatched `}` found -//~|WARN unmatched `}` found trait ImportantTrait1 {} #[diagnostic::on_unimplemented(message = "Test {}")] //~^WARN positional format arguments are not allowed here -//~|WARN positional format arguments are not allowed here trait ImportantTrait2 {} #[diagnostic::on_unimplemented(message = "Test {1:}")] //~^WARN positional format arguments are not allowed here -//~|WARN positional format arguments are not allowed here -//~|WARN invalid format specifier [malformed_diagnostic_format_literals] //~|WARN invalid format specifier [malformed_diagnostic_format_literals] trait ImportantTrait3 {} #[diagnostic::on_unimplemented(message = "Test {Self:123}")] //~^WARN invalid format specifier -//~|WARN invalid format specifier trait ImportantTrait4 {} #[diagnostic::on_unimplemented(message = "Test {Self:!}")] //~^WARN invalid format specifier [malformed_diagnostic_format_literals] -//~|WARN invalid format specifier [malformed_diagnostic_format_literals] trait ImportantTrait5 {} #[diagnostic::on_unimplemented(message = "Test {Self:}")] //~^WARN invalid format specifier [malformed_diagnostic_format_literals] -//~|WARN invalid format specifier [malformed_diagnostic_format_literals] trait ImportantTrait6 {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr index 2138d591ca207..a6ab2f245a3e4 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr @@ -2,12 +2,14 @@ warning: unmatched `}` found --> $DIR/broken_format.rs:2:42 | LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")] - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^-^^^^^^^ + | | + | unmatched `}` | = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: positional format arguments are not allowed here - --> $DIR/broken_format.rs:7:49 + --> $DIR/broken_format.rs:6:49 | LL | #[diagnostic::on_unimplemented(message = "Test {}")] | ^ @@ -15,7 +17,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {}")] = help: only named format arguments with the name of one of the generic types are allowed in this context warning: invalid format specifier - --> $DIR/broken_format.rs:12:50 + --> $DIR/broken_format.rs:10:50 | LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] | ^ @@ -23,7 +25,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] = help: no format specifier are supported in this position warning: positional format arguments are not allowed here - --> $DIR/broken_format.rs:12:49 + --> $DIR/broken_format.rs:10:49 | LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] | ^ @@ -31,7 +33,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] = help: only named format arguments with the name of one of the generic types are allowed in this context warning: invalid format specifier - --> $DIR/broken_format.rs:19:53 + --> $DIR/broken_format.rs:15:53 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] | ^^^^ @@ -39,7 +41,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] = help: no format specifier are supported in this position warning: invalid format specifier - --> $DIR/broken_format.rs:24:53 + --> $DIR/broken_format.rs:19:53 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] | ^^ @@ -47,23 +49,15 @@ LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] = help: no format specifier are supported in this position warning: invalid format specifier - --> $DIR/broken_format.rs:29:53 + --> $DIR/broken_format.rs:23:53 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:}")] | ^ | = help: no format specifier are supported in this position -warning: unmatched `}` found - --> $DIR/broken_format.rs:2:42 - | -LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")] - | ^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: {{Test } thing - --> $DIR/broken_format.rs:43:13 + --> $DIR/broken_format.rs:36:13 | LL | check_1(()); | ------- ^^ the trait `ImportantTrait1` is not implemented for `()` @@ -71,27 +65,18 @@ LL | check_1(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:5:1 + --> $DIR/broken_format.rs:4:1 | LL | trait ImportantTrait1 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_1` - --> $DIR/broken_format.rs:35:20 + --> $DIR/broken_format.rs:28:20 | LL | fn check_1(_: impl ImportantTrait1) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_1` -warning: positional format arguments are not allowed here - --> $DIR/broken_format.rs:7:49 - | -LL | #[diagnostic::on_unimplemented(message = "Test {}")] - | ^ - | - = help: only named format arguments with the name of one of the generic types are allowed in this context - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Test {} - --> $DIR/broken_format.rs:45:13 + --> $DIR/broken_format.rs:38:13 | LL | check_2(()); | ------- ^^ the trait `ImportantTrait2` is not implemented for `()` @@ -99,36 +84,18 @@ LL | check_2(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:10:1 + --> $DIR/broken_format.rs:8:1 | LL | trait ImportantTrait2 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_2` - --> $DIR/broken_format.rs:36:20 + --> $DIR/broken_format.rs:29:20 | LL | fn check_2(_: impl ImportantTrait2) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_2` -warning: invalid format specifier - --> $DIR/broken_format.rs:12:50 - | -LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] - | ^ - | - = help: no format specifier are supported in this position - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: positional format arguments are not allowed here - --> $DIR/broken_format.rs:12:49 - | -LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] - | ^ - | - = help: only named format arguments with the name of one of the generic types are allowed in this context - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Test {1} - --> $DIR/broken_format.rs:47:13 + --> $DIR/broken_format.rs:40:13 | LL | check_3(()); | ------- ^^ the trait `ImportantTrait3` is not implemented for `()` @@ -136,27 +103,18 @@ LL | check_3(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:17:1 + --> $DIR/broken_format.rs:13:1 | LL | trait ImportantTrait3 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_3` - --> $DIR/broken_format.rs:37:20 + --> $DIR/broken_format.rs:30:20 | LL | fn check_3(_: impl ImportantTrait3) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_3` -warning: invalid format specifier - --> $DIR/broken_format.rs:19:53 - | -LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] - | ^^^^ - | - = help: no format specifier are supported in this position - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Test () - --> $DIR/broken_format.rs:49:13 + --> $DIR/broken_format.rs:42:13 | LL | check_4(()); | ------- ^^ the trait `ImportantTrait4` is not implemented for `()` @@ -164,27 +122,18 @@ LL | check_4(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:22:1 + --> $DIR/broken_format.rs:17:1 | LL | trait ImportantTrait4 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_4` - --> $DIR/broken_format.rs:38:20 + --> $DIR/broken_format.rs:31:20 | LL | fn check_4(_: impl ImportantTrait4) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_4` -warning: invalid format specifier - --> $DIR/broken_format.rs:24:53 - | -LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] - | ^^ - | - = help: no format specifier are supported in this position - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Test () - --> $DIR/broken_format.rs:51:13 + --> $DIR/broken_format.rs:44:13 | LL | check_5(()); | ------- ^^ the trait `ImportantTrait5` is not implemented for `()` @@ -192,27 +141,18 @@ LL | check_5(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:27:1 + --> $DIR/broken_format.rs:21:1 | LL | trait ImportantTrait5 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_5` - --> $DIR/broken_format.rs:39:20 + --> $DIR/broken_format.rs:32:20 | LL | fn check_5(_: impl ImportantTrait5) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_5` -warning: invalid format specifier - --> $DIR/broken_format.rs:29:53 - | -LL | #[diagnostic::on_unimplemented(message = "Test {Self:}")] - | ^ - | - = help: no format specifier are supported in this position - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Test () - --> $DIR/broken_format.rs:53:13 + --> $DIR/broken_format.rs:46:13 | LL | check_6(()); | ------- ^^ the trait `ImportantTrait6` is not implemented for `()` @@ -220,16 +160,16 @@ LL | check_6(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:32:1 + --> $DIR/broken_format.rs:25:1 | LL | trait ImportantTrait6 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_6` - --> $DIR/broken_format.rs:40:20 + --> $DIR/broken_format.rs:33:20 | LL | fn check_6(_: impl ImportantTrait6) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_6` -error: aborting due to 6 previous errors; 14 warnings emitted +error: aborting due to 6 previous errors; 7 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs index a0e497fa045b3..e64e60f30c0c1 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs @@ -5,22 +5,18 @@ #[diagnostic::on_unimplemented( on(Self = "&str"), //~^WARN malformed `on_unimplemented` attribute - //~|WARN malformed `on_unimplemented` attribute message = "trait has `{Self}` and `{T}` as params", label = "trait has `{Self}` and `{T}` as params", note = "trait has `{Self}` and `{T}` as params", parent_label = "in this scope", //~^WARN malformed `on_unimplemented` attribute - //~|WARN malformed `on_unimplemented` attribute append_const_msg //~^WARN malformed `on_unimplemented` attribute - //~|WARN malformed `on_unimplemented` attribute )] trait Foo {} #[diagnostic::on_unimplemented = "Message"] //~^WARN malformed `on_unimplemented` attribute -//~|WARN malformed `on_unimplemented` attribute trait Bar {} #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")] @@ -32,27 +28,16 @@ impl Bar for i32 {} #[diagnostic::on_unimplemented( message = "{from_desugaring}{direct}{cause}{integral}{integer}", //~^WARN there is no parameter `from_desugaring` on trait `Baz` - //~|WARN there is no parameter `from_desugaring` on trait `Baz` //~|WARN there is no parameter `direct` on trait `Baz` - //~|WARN there is no parameter `direct` on trait `Baz` - //~|WARN there is no parameter `cause` on trait `Baz` //~|WARN there is no parameter `cause` on trait `Baz` //~|WARN there is no parameter `integral` on trait `Baz` - //~|WARN there is no parameter `integral` on trait `Baz` - //~|WARN there is no parameter `integer` on trait `Baz` //~|WARN there is no parameter `integer` on trait `Baz` label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" //~^WARN there is no parameter `float` on trait `Baz` - //~|WARN there is no parameter `float` on trait `Baz` - //~|WARN there is no parameter `_Self` on trait `Baz` //~|WARN there is no parameter `_Self` on trait `Baz` //~|WARN there is no parameter `crate_local` on trait `Baz` - //~|WARN there is no parameter `crate_local` on trait `Baz` - //~|WARN there is no parameter `Trait` on trait `Baz` //~|WARN there is no parameter `Trait` on trait `Baz` //~|WARN there is no parameter `ItemContext` on trait `Baz` - //~|WARN there is no parameter `ItemContext` on trait `Baz` - //~|WARN there is no parameter `This` on trait `Baz` //~|WARN there is no parameter `This` on trait `Baz` )] trait Baz {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr index eaf1de9ccadab..862f805e15ba7 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr @@ -1,46 +1,13 @@ warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:26:1 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:22:1 | LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:6:5 - | -LL | on(Self = "&str"), - | ^^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default - -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:12:5 - | -LL | parent_label = "in this scope", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:15:5 - | -LL | append_const_msg - | ^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:21:32 - | -LL | #[diagnostic::on_unimplemented = "Message"] - | ^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - warning: there is no parameter `from_desugaring` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:17 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:17 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", | ^^^^^^^^^^^^^^^ @@ -49,7 +16,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: there is no parameter `direct` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:34 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:34 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", | ^^^^^^ @@ -57,7 +24,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `cause` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:42 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:42 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", | ^^^^^ @@ -65,7 +32,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `integral` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:49 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:49 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", | ^^^^^^^^ @@ -73,7 +40,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `integer` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:59 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:59 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", | ^^^^^^^ @@ -81,7 +48,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `float` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:15 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ @@ -89,7 +56,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `_Self` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:22 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ @@ -97,7 +64,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `crate_local` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:29 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:29 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^^^^^^^ @@ -105,7 +72,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `Trait` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:42 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:42 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ @@ -113,7 +80,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `ItemContext` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:49 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:49 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^^^^^^^ @@ -121,7 +88,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `This` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:62 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:62 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^ @@ -135,28 +102,34 @@ LL | on(Self = "&str"), | ^^^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:12:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:11:5 | LL | parent_label = "in this scope", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:15:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:13:5 | LL | append_const_msg | ^^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:18:1 + | +LL | #[diagnostic::on_unimplemented = "Message"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options error[E0277]: trait has `()` and `i32` as params - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:65:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:50:15 | LL | takes_foo(()); | --------- ^^ trait has `()` and `i32` as params @@ -166,27 +139,18 @@ LL | takes_foo(()); = help: the trait `Foo` is not implemented for `()` = note: trait has `()` and `i32` as params help: this trait has no implementations, consider adding one - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:19:1 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:16:1 | LL | trait Foo {} | ^^^^^^^^^^^^ note: required by a bound in `takes_foo` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:60:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:45:22 | LL | fn takes_foo(_: impl Foo) {} | ^^^^^^^^ required by this bound in `takes_foo` -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:21:32 - | -LL | #[diagnostic::on_unimplemented = "Message"] - | ^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:67:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:52:15 | LL | takes_bar(()); | --------- ^^ the trait `Bar` is not implemented for `()` @@ -194,117 +158,18 @@ LL | takes_bar(()); | required by a bound introduced by this call | help: the trait `Bar` is implemented for `i32` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:28:1 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:24:1 | LL | impl Bar for i32 {} | ^^^^^^^^^^^^^^^^ note: required by a bound in `takes_bar` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:61:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:46:22 | LL | fn takes_bar(_: impl Bar) {} | ^^^ required by this bound in `takes_bar` -warning: there is no parameter `from_desugaring` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:17 - | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `direct` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:34 - | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `cause` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:42 - | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `integral` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:49 - | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `integer` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:59 - | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `float` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:15 - | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `_Self` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:22 - | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `crate_local` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:29 - | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `Trait` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:42 - | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `ItemContext` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:49 - | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `This` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:62 - | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: {from_desugaring}{direct}{cause}{integral}{integer} - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:69:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:54:15 | LL | takes_baz(()); | --------- ^^ {float}{_Self}{crate_local}{Trait}{ItemContext}{This} @@ -313,16 +178,16 @@ LL | takes_baz(()); | = help: the trait `Baz` is not implemented for `()` help: this trait has no implementations, consider adding one - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:58:1 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:43:1 | LL | trait Baz {} | ^^^^^^^^^ note: required by a bound in `takes_baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:62:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:47:22 | LL | fn takes_baz(_: impl Baz) {} | ^^^ required by this bound in `takes_baz` -error: aborting due to 3 previous errors; 31 warnings emitted +error: aborting due to 3 previous errors; 16 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs index 08eb5707e909e..c759acc12565c 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs @@ -2,7 +2,6 @@ //@ reference: attributes.diagnostic.on_unimplemented.unknown-keys #[diagnostic::on_unimplemented(unsupported = "foo")] //~^WARN malformed `on_unimplemented` attribute -//~|WARN malformed `on_unimplemented` attribute trait Foo {} #[diagnostic::on_unimplemented(message = "Baz")] @@ -11,12 +10,10 @@ struct Bar {} #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] //~^WARN malformed `on_unimplemented` attribute -//~|WARN malformed `on_unimplemented` attribute trait Baz {} #[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = "whatever"))] //~^WARN malformed `on_unimplemented` attribute -//~|WARN malformed `on_unimplemented` attribute trait Boom {} #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] @@ -29,12 +26,10 @@ trait Doom {} #[diagnostic::on_unimplemented] //~^WARN missing options for `on_unimplemented` attribute -//~|WARN missing options for `on_unimplemented` attribute trait Whatever {} #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] //~^WARN there is no parameter `DoesNotExist` on trait `Test` -//~|WARN there is no parameter `DoesNotExist` on trait `Test` trait Test {} fn take_foo(_: impl Foo) {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr index df5d0d6dccb40..4361e3261a0c1 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr @@ -1,11 +1,20 @@ warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:8:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:7:1 | LL | #[diagnostic::on_unimplemented(message = "Baz")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default +warning: there is no parameter `DoesNotExist` on trait `Test` + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:44 + | +LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] + | ^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + warning: malformed `on_unimplemented` attribute --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32 | @@ -16,7 +25,7 @@ LL | #[diagnostic::on_unimplemented(unsupported = "foo")] = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:11:50 | LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] | ^^^^^^^^^^^^^^^^^^^ invalid option found here @@ -24,7 +33,7 @@ LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] = help: only `message`, `note` and `label` are allowed as options warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:15:50 | LL | #[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = "whatever"))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here @@ -32,7 +41,7 @@ LL | #[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = = help: only `message`, `note` and `label` are allowed as options warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:22:50 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:19:50 | LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here @@ -40,41 +49,23 @@ LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = help: only `message`, `note` and `label` are allowed as options warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:26:32 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:23:1 | LL | #[diagnostic::on_unimplemented = "boom"] - | ^^^^^^^^ invalid option found here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options warning: missing options for `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:30:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:27:1 | LL | #[diagnostic::on_unimplemented] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: at least one of the `message`, `note` and `label` options are expected -warning: there is no parameter `DoesNotExist` on trait `Test` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:35:44 - | -LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] - | ^^^^^^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default - -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32 - | -LL | #[diagnostic::on_unimplemented(unsupported = "foo")] - | ^^^^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: the trait bound `i32: Foo` is not satisfied - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:47:14 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:42:14 | LL | take_foo(1_i32); | -------- ^^^^^ the trait `Foo` is not implemented for `i32` @@ -82,27 +73,18 @@ LL | take_foo(1_i32); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:6:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:5:1 | LL | trait Foo {} | ^^^^^^^^^ note: required by a bound in `take_foo` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:40:21 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:35:21 | LL | fn take_foo(_: impl Foo) {} | ^^^ required by this bound in `take_foo` -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50 - | -LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] - | ^^^^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Boom - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:49:14 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:44:14 | LL | take_baz(1_i32); | -------- ^^^^^ the trait `Baz` is not implemented for `i32` @@ -110,27 +92,18 @@ LL | take_baz(1_i32); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:15:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:13:1 | LL | trait Baz {} | ^^^^^^^^^ note: required by a bound in `take_baz` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:41:21 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:36:21 | LL | fn take_baz(_: impl Baz) {} | ^^^ required by this bound in `take_baz` -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50 - | -LL | #[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = "whatever"))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Boom - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:51:15 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:46:15 | LL | take_boom(1_i32); | --------- ^^^^^ the trait `Boom` is not implemented for `i32` @@ -138,27 +111,18 @@ LL | take_boom(1_i32); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:20:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:1 | LL | trait Boom {} | ^^^^^^^^^^ note: required by a bound in `take_boom` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:42:22 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:37:22 | LL | fn take_boom(_: impl Boom) {} | ^^^^ required by this bound in `take_boom` -warning: missing options for `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:30:1 - | -LL | #[diagnostic::on_unimplemented] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: at least one of the `message`, `note` and `label` options are expected - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: the trait bound `i32: Whatever` is not satisfied - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:53:19 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:48:19 | LL | take_whatever(1_i32); | ------------- ^^^^^ the trait `Whatever` is not implemented for `i32` @@ -166,27 +130,18 @@ LL | take_whatever(1_i32); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:29:1 | LL | trait Whatever {} | ^^^^^^^^^^^^^^ note: required by a bound in `take_whatever` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:43:26 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:38:26 | LL | fn take_whatever(_: impl Whatever) {} | ^^^^^^^^ required by this bound in `take_whatever` -warning: there is no parameter `DoesNotExist` on trait `Test` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:35:44 - | -LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] - | ^^^^^^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: {DoesNotExist} - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:55:15 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:50:15 | LL | take_test(()); | --------- ^^ the trait `Test` is not implemented for `()` @@ -194,16 +149,16 @@ LL | take_test(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:38:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:1 | LL | trait Test {} | ^^^^^^^^^^ note: required by a bound in `take_test` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:44:22 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:39:22 | LL | fn take_test(_: impl Test) {} | ^^^^ required by this bound in `take_test` -error: aborting due to 5 previous errors; 13 warnings emitted +error: aborting due to 5 previous errors; 8 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs deleted file mode 100644 index b06f56bd66e44..0000000000000 --- a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ reference: attributes.diagnostic.on_unimplemented.repetition -//@ reference: attributes.diagnostic.on_unimplemented.syntax -#[diagnostic::on_unimplemented( - if(Self = "()"), - //~^WARN malformed `on_unimplemented` attribute - //~|WARN malformed `on_unimplemented` attribute - message = "custom message", - note = "custom note" -)] -#[diagnostic::on_unimplemented(message = "fallback!!")] -//~^ WARN `message` is ignored due to previous definition of `message` -//~| WARN `message` is ignored due to previous definition of `message` -#[diagnostic::on_unimplemented(label = "fallback label")] -#[diagnostic::on_unimplemented(note = "fallback note")] -trait Foo {} - -fn takes_foo(_: impl Foo) {} - -fn main() { - takes_foo(()); - //~^ERROR custom message -} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr deleted file mode 100644 index 24b3a9c3405fa..0000000000000 --- a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr +++ /dev/null @@ -1,63 +0,0 @@ -warning: malformed `on_unimplemented` attribute - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:4:5 - | -LL | if(Self = "()"), - | ^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default - -warning: `message` is ignored due to previous definition of `message` - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:10:32 - | -LL | message = "custom message", - | -------------------------- `message` is first declared here -... -LL | #[diagnostic::on_unimplemented(message = "fallback!!")] - | ^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here - -warning: malformed `on_unimplemented` attribute - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:4:5 - | -LL | if(Self = "()"), - | ^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: `message` is ignored due to previous definition of `message` - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:10:32 - | -LL | message = "custom message", - | -------------------------- `message` is first declared here -... -LL | #[diagnostic::on_unimplemented(message = "fallback!!")] - | ^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0277]: custom message - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:20:15 - | -LL | takes_foo(()); - | --------- ^^ fallback label - | | - | required by a bound introduced by this call - | - = help: the trait `Foo` is not implemented for `()` - = note: custom note - = note: fallback note -help: this trait has no implementations, consider adding one - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:1 - | -LL | trait Foo {} - | ^^^^^^^^^ -note: required by a bound in `takes_foo` - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:17:22 - | -LL | fn takes_foo(_: impl Foo) {} - | ^^^ required by this bound in `takes_foo` - -error: aborting due to 1 previous error; 4 warnings emitted - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.rs b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.rs index d0eb608c40f20..414b7ec93b51f 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.rs @@ -7,10 +7,8 @@ #[diagnostic::on_unimplemented( message = "second message", //~^WARN `message` is ignored due to previous definition of `message` - //~|WARN `message` is ignored due to previous definition of `message` label = "second label", //~^WARN `label` is ignored due to previous definition of `label` - //~|WARN `label` is ignored due to previous definition of `label` note = "second note" )] trait Foo {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr index de43656ff085b..2f20777c8f2f5 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr @@ -5,43 +5,21 @@ LL | message = "first message", | ------------------------- `message` is first declared here ... LL | message = "second message", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is later redundantly declared here | = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: `label` is ignored due to previous definition of `label` - --> $DIR/report_warning_on_duplicated_options.rs:11:5 + --> $DIR/report_warning_on_duplicated_options.rs:10:5 | LL | label = "first label", | --------------------- `label` is first declared here ... LL | label = "second label", - | ^^^^^^^^^^^^^^^^^^^^^^ `label` is already declared here - -warning: `message` is ignored due to previous definition of `message` - --> $DIR/report_warning_on_duplicated_options.rs:8:5 - | -LL | message = "first message", - | ------------------------- `message` is first declared here -... -LL | message = "second message", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: `label` is ignored due to previous definition of `label` - --> $DIR/report_warning_on_duplicated_options.rs:11:5 - | -LL | label = "first label", - | --------------------- `label` is first declared here -... -LL | label = "second label", - | ^^^^^^^^^^^^^^^^^^^^^^ `label` is already declared here - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + | ^^^^^^^^^^^^^^^^^^^^^^ `label` is later redundantly declared here error[E0277]: first message - --> $DIR/report_warning_on_duplicated_options.rs:22:15 + --> $DIR/report_warning_on_duplicated_options.rs:20:15 | LL | takes_foo(()); | --------- ^^ first label @@ -52,16 +30,16 @@ LL | takes_foo(()); = note: custom note = note: second note help: this trait has no implementations, consider adding one - --> $DIR/report_warning_on_duplicated_options.rs:16:1 + --> $DIR/report_warning_on_duplicated_options.rs:14:1 | LL | trait Foo {} | ^^^^^^^^^ note: required by a bound in `takes_foo` - --> $DIR/report_warning_on_duplicated_options.rs:19:22 + --> $DIR/report_warning_on_duplicated_options.rs:17:22 | LL | fn takes_foo(_: impl Foo) {} | ^^^ required by this bound in `takes_foo` -error: aborting due to 1 previous error; 4 warnings emitted +error: aborting due to 1 previous error; 2 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/on-unimplemented/bad-annotation.rs b/tests/ui/on-unimplemented/bad-annotation.rs index 937e5b82da50c..c8d846a2273df 100644 --- a/tests/ui/on-unimplemented/bad-annotation.rs +++ b/tests/ui/on-unimplemented/bad-annotation.rs @@ -2,26 +2,28 @@ #![feature(rustc_attrs)] #![allow(unused)] -#[rustc_on_unimplemented = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}`"] +#[rustc_on_unimplemented(label = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}`")] trait Foo {} -#[rustc_on_unimplemented = "a collection of type `{Self}` cannot \ - be built from an iterator over elements of type `{A}`"] +#[rustc_on_unimplemented(label = "a collection of type `{Self}` cannot \ + be built from an iterator over elements of type `{A}`")] trait MyFromIterator { /// Builds a container with elements from an external iterator. fn my_from_iter>(iterator: T) -> Self; } #[rustc_on_unimplemented] -//~^ ERROR malformed `rustc_on_unimplemented` attribute +//~^ WARN missing options for `on_unimplemented` attribute +//~| NOTE part of trait NoContent {} -#[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"] -//~^ ERROR cannot find parameter C on this trait +#[rustc_on_unimplemented(label = "Unimplemented error on `{Self}` with params `<{A},{B},{C}>`")] +//~^ WARN there is no parameter `C` on trait `ParameterNotPresent` +//~| NOTE part of trait ParameterNotPresent {} -#[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{}>`"] -//~^ ERROR positional format arguments are not allowed here +#[rustc_on_unimplemented(label = "Unimplemented error on `{Self}` with params `<{A},{B},{}>`")] +//~^ WARN positional format arguments are not allowed here trait NoPositionalArgs {} #[rustc_on_unimplemented(lorem = "")] @@ -43,9 +45,8 @@ trait Invalid {} trait DuplicateMessage {} #[rustc_on_unimplemented(message = "x", on(desugared, message = "y"))] -//~^ ERROR this attribute must have a value -//~^^ NOTE e.g. `#[rustc_on_unimplemented(message="foo")]` -//~^^^ NOTE expected value here +//~^ ERROR invalid flag in `on`-clause +//~| NOTE expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `desugared` trait OnInWrongPosition {} #[rustc_on_unimplemented(on(), message = "y")] @@ -60,6 +61,9 @@ trait EmptyOn {} trait ExpectedPredicateInOn {} #[rustc_on_unimplemented(on(Self = "y"), message = "y")] +//~^ ERROR this attribute must have a value +//~| NOTE expected value here +//~| NOTE e.g. `#[rustc_on_unimplemented(message="foo")]` trait OnWithoutDirectives {} #[rustc_on_unimplemented(on(from_desugaring, on(from_desugaring, message = "x")), message = "y")] @@ -109,11 +113,9 @@ trait InvalidPredicate {} trait InvalidFlag {} #[rustc_on_unimplemented(on(_Self = "y", message = "y"))] -//~^ ERROR invalid name in `on`-clause -//~^^ NOTE expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `_Self` +//~^ WARN there is no parameter `_Self` on trait `InvalidName` trait InvalidName {} #[rustc_on_unimplemented(on(abc = "y", message = "y"))] -//~^ ERROR invalid name in `on`-clause -//~^^ NOTE expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `abc` +//~^ WARN there is no parameter `abc` on trait `InvalidName2` trait InvalidName2 {} diff --git a/tests/ui/on-unimplemented/bad-annotation.stderr b/tests/ui/on-unimplemented/bad-annotation.stderr index 3fc5453277404..6316dd6aa2d27 100644 --- a/tests/ui/on-unimplemented/bad-annotation.stderr +++ b/tests/ui/on-unimplemented/bad-annotation.stderr @@ -1,30 +1,5 @@ -error: malformed `rustc_on_unimplemented` attribute input - --> $DIR/bad-annotation.rs:15:1 - | -LL | #[rustc_on_unimplemented] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the following are the possible correct uses - | -LL | #[rustc_on_unimplemented = "message"] - | +++++++++++ -LL | #[rustc_on_unimplemented(/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...")] - | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -error[E0230]: cannot find parameter C on this trait - --> $DIR/bad-annotation.rs:19:90 - | -LL | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"] - | ^ - -error[E0231]: positional format arguments are not allowed here - --> $DIR/bad-annotation.rs:23:90 - | -LL | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{}>`"] - | ^ - error[E0232]: this attribute must have a value - --> $DIR/bad-annotation.rs:27:26 + --> $DIR/bad-annotation.rs:29:26 | LL | #[rustc_on_unimplemented(lorem = "")] | ^^^^^^^^^^ expected value here @@ -32,7 +7,7 @@ LL | #[rustc_on_unimplemented(lorem = "")] = note: e.g. `#[rustc_on_unimplemented(message="foo")]` error[E0232]: this attribute must have a value - --> $DIR/bad-annotation.rs:33:26 + --> $DIR/bad-annotation.rs:35:26 | LL | #[rustc_on_unimplemented(lorem(ipsum(dolor)))] | ^^^^^^^^^^^^^^^^^^^ expected value here @@ -40,29 +15,27 @@ LL | #[rustc_on_unimplemented(lorem(ipsum(dolor)))] = note: e.g. `#[rustc_on_unimplemented(message="foo")]` error[E0232]: this attribute must have a value - --> $DIR/bad-annotation.rs:39:41 + --> $DIR/bad-annotation.rs:41:41 | LL | #[rustc_on_unimplemented(message = "x", message = "y")] | ^^^^^^^^^^^^^ expected value here | = note: e.g. `#[rustc_on_unimplemented(message="foo")]` -error[E0232]: this attribute must have a value - --> $DIR/bad-annotation.rs:45:41 +error[E0232]: invalid flag in `on`-clause + --> $DIR/bad-annotation.rs:47:44 | LL | #[rustc_on_unimplemented(message = "x", on(desugared, message = "y"))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected value here - | - = note: e.g. `#[rustc_on_unimplemented(message="foo")]` + | ^^^^^^^^^ expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `desugared` error[E0232]: empty `on`-clause in `#[rustc_on_unimplemented]` - --> $DIR/bad-annotation.rs:51:26 + --> $DIR/bad-annotation.rs:52:26 | LL | #[rustc_on_unimplemented(on(), message = "y")] | ^^^^ empty `on`-clause here error[E0232]: this attribute must have a value - --> $DIR/bad-annotation.rs:56:26 + --> $DIR/bad-annotation.rs:57:26 | LL | #[rustc_on_unimplemented(on = "x", message = "y")] | ^^^^^^^^ expected value here @@ -70,7 +43,15 @@ LL | #[rustc_on_unimplemented(on = "x", message = "y")] = note: e.g. `#[rustc_on_unimplemented(message="foo")]` error[E0232]: this attribute must have a value - --> $DIR/bad-annotation.rs:65:46 + --> $DIR/bad-annotation.rs:63:26 + | +LL | #[rustc_on_unimplemented(on(Self = "y"), message = "y")] + | ^^^^^^^^^^^^^^ expected value here + | + = note: e.g. `#[rustc_on_unimplemented(message="foo")]` + +error[E0232]: this attribute must have a value + --> $DIR/bad-annotation.rs:69:46 | LL | #[rustc_on_unimplemented(on(from_desugaring, on(from_desugaring, message = "x")), message = "y")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected value here @@ -78,66 +59,90 @@ LL | #[rustc_on_unimplemented(on(from_desugaring, on(from_desugaring, message = = note: e.g. `#[rustc_on_unimplemented(message="foo")]` error[E0232]: literals inside `on`-clauses are not supported - --> $DIR/bad-annotation.rs:71:29 + --> $DIR/bad-annotation.rs:75:29 | LL | #[rustc_on_unimplemented(on("y", message = "y"))] | ^^^ unexpected literal here error[E0232]: literals inside `on`-clauses are not supported - --> $DIR/bad-annotation.rs:76:29 + --> $DIR/bad-annotation.rs:80:29 | LL | #[rustc_on_unimplemented(on(42, message = "y"))] | ^^ unexpected literal here error[E0232]: expected a single predicate in `not(..)` - --> $DIR/bad-annotation.rs:81:33 + --> $DIR/bad-annotation.rs:85:32 | LL | #[rustc_on_unimplemented(on(not(a, b), message = "y"))] - | ^^^^ unexpected quantity of predicates here + | ^^^^^^ unexpected quantity of predicates here error[E0232]: expected a single predicate in `not(..)` - --> $DIR/bad-annotation.rs:86:29 + --> $DIR/bad-annotation.rs:90:32 | LL | #[rustc_on_unimplemented(on(not(), message = "y"))] - | ^^^^^ unexpected quantity of predicates here + | ^^ unexpected quantity of predicates here error[E0232]: expected an identifier inside this `on`-clause - --> $DIR/bad-annotation.rs:91:29 + --> $DIR/bad-annotation.rs:95:29 | LL | #[rustc_on_unimplemented(on(thing::What, message = "y"))] | ^^^^^^^^^^^ expected an identifier here, not `thing::What` error[E0232]: expected an identifier inside this `on`-clause - --> $DIR/bad-annotation.rs:96:29 + --> $DIR/bad-annotation.rs:100:29 | LL | #[rustc_on_unimplemented(on(thing::What = "value", message = "y"))] | ^^^^^^^^^^^ expected an identifier here, not `thing::What` error[E0232]: this predicate is invalid - --> $DIR/bad-annotation.rs:101:29 + --> $DIR/bad-annotation.rs:105:29 | LL | #[rustc_on_unimplemented(on(aaaaaaaaaaaaaa(a, b), message = "y"))] | ^^^^^^^^^^^^^^ expected one of `any`, `all` or `not` here, not `aaaaaaaaaaaaaa` error[E0232]: invalid flag in `on`-clause - --> $DIR/bad-annotation.rs:106:29 + --> $DIR/bad-annotation.rs:110:29 | LL | #[rustc_on_unimplemented(on(something, message = "y"))] | ^^^^^^^^^ expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `something` -error[E0232]: invalid name in `on`-clause - --> $DIR/bad-annotation.rs:111:29 +warning: there is no parameter `C` on trait `ParameterNotPresent` + --> $DIR/bad-annotation.rs:20:90 + | +LL | #[rustc_on_unimplemented(label = "Unimplemented error on `{Self}` with params `<{A},{B},{C}>`")] + | ^ + | + = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: there is no parameter `_Self` on trait `InvalidName` + --> $DIR/bad-annotation.rs:115:29 | LL | #[rustc_on_unimplemented(on(_Self = "y", message = "y"))] - | ^^^^^ expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `_Self` + | ^^^^^^^^^^^ -error[E0232]: invalid name in `on`-clause - --> $DIR/bad-annotation.rs:116:29 +warning: there is no parameter `abc` on trait `InvalidName2` + --> $DIR/bad-annotation.rs:119:29 | LL | #[rustc_on_unimplemented(on(abc = "y", message = "y"))] - | ^^^ expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `abc` + | ^^^^^^^^^ + +warning: missing options for `on_unimplemented` attribute + --> $DIR/bad-annotation.rs:15:1 + | +LL | #[rustc_on_unimplemented] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least one of the `message`, `note` and `label` options are expected + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: positional format arguments are not allowed here + --> $DIR/bad-annotation.rs:25:90 + | +LL | #[rustc_on_unimplemented(label = "Unimplemented error on `{Self}` with params `<{A},{B},{}>`")] + | ^ + | + = help: only named format arguments with the name of one of the generic types are allowed in this context -error: aborting due to 20 previous errors +error: aborting due to 16 previous errors; 5 warnings emitted -Some errors have detailed explanations: E0230, E0231, E0232. -For more information about an error, try `rustc --explain E0230`. +For more information about this error, try `rustc --explain E0232`. diff --git a/tests/ui/on-unimplemented/feature-gate-on-unimplemented.rs b/tests/ui/on-unimplemented/feature-gate-on-unimplemented.rs index 436caab5d64aa..1fdb1d2f2c0cb 100644 --- a/tests/ui/on-unimplemented/feature-gate-on-unimplemented.rs +++ b/tests/ui/on-unimplemented/feature-gate-on-unimplemented.rs @@ -1,6 +1,6 @@ // Test that `#[rustc_on_unimplemented]` is gated by `rustc_attrs` feature gate. -#[rustc_on_unimplemented = "test error `{Self}` with `{Bar}`"] +#[rustc_on_unimplemented(label = "test error `{Self}` with `{Bar}`")] //~^ ERROR use of an internal attribute [E0658] //~| NOTE the `#[rustc_on_unimplemented]` attribute is an internal implementation detail that will never be stable //~| NOTE see `#[diagnostic::on_unimplemented]` for the stable equivalent of this attribute diff --git a/tests/ui/on-unimplemented/feature-gate-on-unimplemented.stderr b/tests/ui/on-unimplemented/feature-gate-on-unimplemented.stderr index d1983088af853..c6680666ece05 100644 --- a/tests/ui/on-unimplemented/feature-gate-on-unimplemented.stderr +++ b/tests/ui/on-unimplemented/feature-gate-on-unimplemented.stderr @@ -1,8 +1,8 @@ error[E0658]: use of an internal attribute --> $DIR/feature-gate-on-unimplemented.rs:3:1 | -LL | #[rustc_on_unimplemented = "test error `{Self}` with `{Bar}`"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[rustc_on_unimplemented(label = "test error `{Self}` with `{Bar}`")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable = note: the `#[rustc_on_unimplemented]` attribute is an internal implementation detail that will never be stable diff --git a/tests/ui/on-unimplemented/on-trait.rs b/tests/ui/on-unimplemented/on-trait.rs index 91630af17e92a..0639d36eff409 100644 --- a/tests/ui/on-unimplemented/on-trait.rs +++ b/tests/ui/on-unimplemented/on-trait.rs @@ -3,7 +3,7 @@ #![feature(rustc_attrs)] pub mod Bar { - #[rustc_on_unimplemented = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}` in `{This}`"] + #[rustc_on_unimplemented(label ="test error `{Self}` with `{Bar}` `{Baz}` `{Quux}` in `{This}`")] pub trait Foo {} } @@ -13,7 +13,7 @@ fn foobar>() -> T { panic!() } -#[rustc_on_unimplemented="a collection of type `{Self}` cannot be built from an iterator over elements of type `{A}`"] +#[rustc_on_unimplemented(label ="a collection of type `{Self}` cannot be built from an iterator over elements of type `{A}`")] trait MyFromIterator { /// Builds a container with elements from an external iterator. fn my_from_iter>(iterator: T) -> Self; diff --git a/tests/ui/tool-attributes/invalid-tool.rs b/tests/ui/tool-attributes/invalid-tool.rs index 125333231d217..aec31cc7f667c 100644 --- a/tests/ui/tool-attributes/invalid-tool.rs +++ b/tests/ui/tool-attributes/invalid-tool.rs @@ -1,6 +1,6 @@ #![feature(register_tool)] #![register_tool(1)] -//~^ ERROR `register_tool` only accepts identifiers +//~^ ERROR malformed `register_tool` attribute input fn main() {} diff --git a/tests/ui/tool-attributes/invalid-tool.stderr b/tests/ui/tool-attributes/invalid-tool.stderr index deafa6d167c20..4f82e9ef5437f 100644 --- a/tests/ui/tool-attributes/invalid-tool.stderr +++ b/tests/ui/tool-attributes/invalid-tool.stderr @@ -1,8 +1,12 @@ -error: `register_tool` only accepts identifiers - --> $DIR/invalid-tool.rs:3:18 +error[E0539]: malformed `register_tool` attribute input + --> $DIR/invalid-tool.rs:3:1 | LL | #![register_tool(1)] - | ^ not an identifier + | ^^^^^^^^^^^^^^^^^-^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#![register_tool(tool1, tool2, ...)]` error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/tool-attributes/nested-disallowed.rs b/tests/ui/tool-attributes/nested-disallowed.rs index 8e78042776106..87d0bf48b6c8f 100644 --- a/tests/ui/tool-attributes/nested-disallowed.rs +++ b/tests/ui/tool-attributes/nested-disallowed.rs @@ -1,4 +1,4 @@ #![feature(register_tool)] -#![register_tool(foo::bar)] //~ ERROR only accepts identifiers +#![register_tool(foo::bar)] //~ ERROR malformed `register_tool` attribute input fn main() {} diff --git a/tests/ui/tool-attributes/nested-disallowed.stderr b/tests/ui/tool-attributes/nested-disallowed.stderr index 1af73fc2f1995..e59ebd979b4c6 100644 --- a/tests/ui/tool-attributes/nested-disallowed.stderr +++ b/tests/ui/tool-attributes/nested-disallowed.stderr @@ -1,8 +1,12 @@ -error: `register_tool` only accepts identifiers - --> $DIR/nested-disallowed.rs:2:18 +error[E0539]: malformed `register_tool` attribute input + --> $DIR/nested-disallowed.rs:2:1 | LL | #![register_tool(foo::bar)] - | ^^^^^^^^ not an identifier + | ^^^^^^^^^^^^^^^^^--------^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#![register_tool(tool1, tool2, ...)]` error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/unpretty/diagnostic-attr.rs b/tests/ui/unpretty/diagnostic-attr.rs index 4ef85c71f90e2..77c58a7025d06 100644 --- a/tests/ui/unpretty/diagnostic-attr.rs +++ b/tests/ui/unpretty/diagnostic-attr.rs @@ -1,6 +1,7 @@ //@ compile-flags: -Zunpretty=hir //@ check-pass //@ edition: 2015 +#![feature(diagnostic_on_const)] #[diagnostic::on_unimplemented( message = "My Message for `ImportantTrait<{A}>` implemented for `{Self}`", @@ -12,3 +13,13 @@ pub trait ImportantTrait {} #[diagnostic::do_not_recommend] impl ImportantTrait for T where T: Clone {} + +pub struct X; + +#[diagnostic::on_const( + message = "My Message for `ImportantTrait` implemented for `{Self}`", + label = "My Label", + note = "Note 1", + note = "Note 2" +)] +impl ImportantTrait for X {} diff --git a/tests/ui/unpretty/diagnostic-attr.stdout b/tests/ui/unpretty/diagnostic-attr.stdout index 0b4b5f9193435..93a4717376fec 100644 --- a/tests/ui/unpretty/diagnostic-attr.stdout +++ b/tests/ui/unpretty/diagnostic-attr.stdout @@ -1,14 +1,32 @@ -extern crate std; -#[attr = PreludeImport] -use ::std::prelude::rust_2015::*; //@ compile-flags: -Zunpretty=hir //@ check-pass //@ edition: 2015 +#![feature(diagnostic_on_const)] +extern crate std; +#[attr = PreludeImport] +use ::std::prelude::rust_2015::*; -#[diagnostic::on_unimplemented(message = -"My Message for `ImportantTrait<{A}>` implemented for `{Self}`", label = -"My Label", note = "Note 1", note = "Note 2")] +#[attr = OnUnimplemented {directive: Directive {is_rustc_attr: false, +subcommands: [], +message: FormatString {input: "My Message for `ImportantTrait<{A}>` implemented for `{Self}`", +pieces: [Lit("My Message for `ImportantTrait<"), +Arg(GenericParam {generic_param: "A"}), Lit(">` implemented for `"), +Arg(SelfUpper), Lit("`")]}, label: FormatString {input: "My Label", +pieces: [Lit("My Label")]}, notes: [FormatString {input: "Note 1", +pieces: [Lit("Note 1")]}, FormatString {input: "Note 2", +pieces: [Lit("Note 2")]}]}}] trait ImportantTrait { } #[attr = DoNotRecommend] impl ImportantTrait for T where T: Clone { } + +struct X; + +#[attr = OnConst {directive: Directive {is_rustc_attr: false, subcommands: [], +message: FormatString {input: "My Message for `ImportantTrait` implemented for `{Self}`", +pieces: [Lit("My Message for `ImportantTrait` implemented for `"), +Arg(SelfUpper), Lit("`")]}, label: FormatString {input: "My Label", +pieces: [Lit("My Label")]}, notes: [FormatString {input: "Note 1", +pieces: [Lit("Note 1")]}, FormatString {input: "Note 2", +pieces: [Lit("Note 2")]}]}}] +impl ImportantTrait for X { }