mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Auto merge of #101225 - matthiaskrgr:rollup-9s1chas, r=matthiaskrgr
Rollup of 8 pull requests Successful merges: - #100970 (Allow deriving multipart suggestions) - #100984 (Reinstate preloading of some dll imports) - #101011 (Use getentropy when possible on all Apple platforms) - #101025 (Add tier-3 support for powerpc64 and riscv64 openbsd) - #101049 (Remove span fatal from ast lowering) - #101100 (Make call suggestions more general and more accurate) - #101171 (Fix UB from misalignment and provenance widening in `std::sys::windows`) - #101185 (Tweak `WellFormedLoc`s a bit) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
12e4fd0755
@ -327,3 +327,10 @@ pub struct ArbitraryExpressionInPattern {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionDiagnostic, Clone, Copy)]
|
||||
#[diag(ast_lowering::inclusive_range_with_no_end)]
|
||||
pub struct InclusiveRangeWithNoEnd {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use super::errors::{
|
||||
AsyncGeneratorsNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
|
||||
BaseExpressionDoubleDot, ClosureCannotBeStatic, FunctionalRecordUpdateDestructuringAssignemnt,
|
||||
GeneratorTooManyParameters, NotSupportedForLifetimeBinderAsyncClosure, RustcBoxAttributeError,
|
||||
UnderscoreExprLhsAssign,
|
||||
GeneratorTooManyParameters, InclusiveRangeWithNoEnd, NotSupportedForLifetimeBinderAsyncClosure,
|
||||
RustcBoxAttributeError, UnderscoreExprLhsAssign,
|
||||
};
|
||||
use super::ResolverAstLoweringExt;
|
||||
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
|
||||
@ -1264,7 +1264,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
(Some(..), Some(..), HalfOpen) => hir::LangItem::Range,
|
||||
(None, Some(..), Closed) => hir::LangItem::RangeToInclusive,
|
||||
(Some(..), Some(..), Closed) => unreachable!(),
|
||||
(_, None, Closed) => self.diagnostic().span_fatal(span, "inclusive range with no end"),
|
||||
(start, None, Closed) => {
|
||||
self.tcx.sess.emit_err(InclusiveRangeWithNoEnd { span });
|
||||
match start {
|
||||
Some(..) => hir::LangItem::RangeFrom,
|
||||
None => hir::LangItem::RangeFull,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let fields = self.arena.alloc_from_iter(
|
||||
|
@ -36,6 +36,8 @@
|
||||
#![feature(never_type)]
|
||||
#![recursion_limit = "256"]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
@ -129,3 +129,5 @@ ast_lowering_not_supported_for_lifetime_binder_async_closure =
|
||||
|
||||
ast_lowering_arbitrary_expression_in_pattern =
|
||||
arbitrary expressions aren't allowed in patterns
|
||||
|
||||
ast_lowering_inclusive_range_with_no_end = inclusive range with no end
|
||||
|
@ -686,19 +686,12 @@ impl Diagnostic {
|
||||
suggestion: Vec<(Span, String)>,
|
||||
applicability: Applicability,
|
||||
) -> &mut Self {
|
||||
assert!(!suggestion.is_empty());
|
||||
self.push_suggestion(CodeSuggestion {
|
||||
substitutions: vec![Substitution {
|
||||
parts: suggestion
|
||||
.into_iter()
|
||||
.map(|(span, snippet)| SubstitutionPart { snippet, span })
|
||||
.collect(),
|
||||
}],
|
||||
msg: self.subdiagnostic_message_to_diagnostic_message(msg),
|
||||
style: SuggestionStyle::CompletelyHidden,
|
||||
self.multipart_suggestion_with_style(
|
||||
msg,
|
||||
suggestion,
|
||||
applicability,
|
||||
});
|
||||
self
|
||||
SuggestionStyle::CompletelyHidden,
|
||||
)
|
||||
}
|
||||
|
||||
/// Prints out a message with a suggested edit of the code.
|
||||
|
@ -1249,9 +1249,13 @@ impl HandlerInner {
|
||||
}
|
||||
|
||||
fn treat_err_as_bug(&self) -> bool {
|
||||
self.flags
|
||||
.treat_err_as_bug
|
||||
.map_or(false, |c| self.err_count() + self.lint_err_count >= c.get())
|
||||
self.flags.treat_err_as_bug.map_or(false, |c| {
|
||||
self.err_count()
|
||||
+ self.lint_err_count
|
||||
+ self.delayed_span_bugs.len()
|
||||
+ self.delayed_good_path_bugs.len()
|
||||
>= c.get()
|
||||
})
|
||||
}
|
||||
|
||||
fn print_error_count(&mut self, registry: &Registry) {
|
||||
@ -1407,7 +1411,14 @@ impl HandlerInner {
|
||||
// This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before
|
||||
// incrementing `err_count` by one, so we need to +1 the comparing.
|
||||
// FIXME: Would be nice to increment err_count in a more coherent way.
|
||||
if self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() + 1 >= c.get()) {
|
||||
if self.flags.treat_err_as_bug.map_or(false, |c| {
|
||||
self.err_count()
|
||||
+ self.lint_err_count
|
||||
+ self.delayed_span_bugs.len()
|
||||
+ self.delayed_good_path_bugs.len()
|
||||
+ 1
|
||||
>= c.get()
|
||||
}) {
|
||||
// FIXME: don't abort here if report_delayed_bugs is off
|
||||
self.span_bug(sp, msg);
|
||||
}
|
||||
|
@ -41,7 +41,8 @@ macro_rules! pluralize {
|
||||
/// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
|
||||
/// to determine whether it should be automatically applied or if the user should be consulted
|
||||
/// before applying the suggestion.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable, Serialize, Deserialize)]
|
||||
#[derive(Copy, Clone, Debug, Hash, Encodable, Decodable, Serialize, Deserialize)]
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Applicability {
|
||||
/// The suggestion is definitely what the user intended, or maintains the exact meaning of the code.
|
||||
/// This suggestion should be automatically applied.
|
||||
|
@ -342,10 +342,10 @@ fn main() {
|
||||
};
|
||||
|
||||
// RISC-V GCC erroneously requires libatomic for sub-word
|
||||
// atomic operations. FreeBSD uses Clang as its system
|
||||
// atomic operations. Some BSD uses Clang as its system
|
||||
// compiler and provides no libatomic in its base system so
|
||||
// does not want this.
|
||||
if !target.contains("freebsd") && target.starts_with("riscv") {
|
||||
if target.starts_with("riscv") && !target.contains("freebsd") && !target.contains("openbsd") {
|
||||
println!("cargo:rustc-link-lib=atomic");
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ use quote::{format_ident, quote};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
use syn::{parse_quote, spanned::Spanned, Meta, MetaList, MetaNameValue, NestedMeta, Path};
|
||||
use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path};
|
||||
use synstructure::{BindingInfo, Structure, VariantInfo};
|
||||
|
||||
/// Which kind of suggestion is being created?
|
||||
@ -28,8 +28,41 @@ enum SubdiagnosticSuggestionKind {
|
||||
Verbose,
|
||||
}
|
||||
|
||||
impl FromStr for SubdiagnosticSuggestionKind {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"" => Ok(SubdiagnosticSuggestionKind::Normal),
|
||||
"_short" => Ok(SubdiagnosticSuggestionKind::Short),
|
||||
"_hidden" => Ok(SubdiagnosticSuggestionKind::Hidden),
|
||||
"_verbose" => Ok(SubdiagnosticSuggestionKind::Verbose),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SubdiagnosticSuggestionKind {
|
||||
pub fn to_suggestion_style(&self) -> TokenStream {
|
||||
match self {
|
||||
SubdiagnosticSuggestionKind::Normal => {
|
||||
quote! { rustc_errors::SuggestionStyle::ShowCode }
|
||||
}
|
||||
SubdiagnosticSuggestionKind::Short => {
|
||||
quote! { rustc_errors::SuggestionStyle::HideCodeInline }
|
||||
}
|
||||
SubdiagnosticSuggestionKind::Hidden => {
|
||||
quote! { rustc_errors::SuggestionStyle::HideCodeAlways }
|
||||
}
|
||||
SubdiagnosticSuggestionKind::Verbose => {
|
||||
quote! { rustc_errors::SuggestionStyle::ShowAlways }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Which kind of subdiagnostic is being created from a variant?
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone)]
|
||||
enum SubdiagnosticKind {
|
||||
/// `#[label(...)]`
|
||||
Label,
|
||||
@ -40,31 +73,9 @@ enum SubdiagnosticKind {
|
||||
/// `#[warning(...)]`
|
||||
Warn,
|
||||
/// `#[suggestion{,_short,_hidden,_verbose}]`
|
||||
Suggestion(SubdiagnosticSuggestionKind),
|
||||
}
|
||||
|
||||
impl FromStr for SubdiagnosticKind {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"label" => Ok(SubdiagnosticKind::Label),
|
||||
"note" => Ok(SubdiagnosticKind::Note),
|
||||
"help" => Ok(SubdiagnosticKind::Help),
|
||||
"warning" => Ok(SubdiagnosticKind::Warn),
|
||||
"suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
|
||||
"suggestion_short" => {
|
||||
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
|
||||
}
|
||||
"suggestion_hidden" => {
|
||||
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden))
|
||||
}
|
||||
"suggestion_verbose" => {
|
||||
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose))
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
Suggestion { suggestion_kind: SubdiagnosticSuggestionKind, code: TokenStream },
|
||||
/// `#[multipart_suggestion{,_short,_hidden,_verbose}]`
|
||||
MultipartSuggestion { suggestion_kind: SubdiagnosticSuggestionKind },
|
||||
}
|
||||
|
||||
impl quote::IdentFragment for SubdiagnosticKind {
|
||||
@ -74,17 +85,9 @@ impl quote::IdentFragment for SubdiagnosticKind {
|
||||
SubdiagnosticKind::Note => write!(f, "note"),
|
||||
SubdiagnosticKind::Help => write!(f, "help"),
|
||||
SubdiagnosticKind::Warn => write!(f, "warn"),
|
||||
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
|
||||
write!(f, "suggestion")
|
||||
}
|
||||
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short) => {
|
||||
write!(f, "suggestion_short")
|
||||
}
|
||||
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden) => {
|
||||
write!(f, "suggestion_hidden")
|
||||
}
|
||||
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose) => {
|
||||
write!(f, "suggestion_verbose")
|
||||
SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestion_with_style"),
|
||||
SubdiagnosticKind::MultipartSuggestion { .. } => {
|
||||
write!(f, "multipart_suggestion_with_style")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -148,11 +151,9 @@ impl<'a> SessionSubdiagnosticDerive<'a> {
|
||||
variant,
|
||||
span,
|
||||
fields: fields_map,
|
||||
kind: None,
|
||||
slug: None,
|
||||
code: None,
|
||||
span_field: None,
|
||||
applicability: None,
|
||||
has_suggestion_parts: false,
|
||||
};
|
||||
builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
|
||||
});
|
||||
@ -193,21 +194,15 @@ struct SessionSubdiagnosticDeriveBuilder<'a> {
|
||||
/// derive builder.
|
||||
fields: HashMap<String, TokenStream>,
|
||||
|
||||
/// Subdiagnostic kind of the type/variant.
|
||||
kind: Option<(SubdiagnosticKind, proc_macro::Span)>,
|
||||
|
||||
/// Slug of the subdiagnostic - corresponds to the Fluent identifier for the message - from the
|
||||
/// `#[kind(slug)]` attribute on the type or variant.
|
||||
slug: Option<(Path, proc_macro::Span)>,
|
||||
/// If a suggestion, the code to suggest as a replacement - from the `#[kind(code = "...")]`
|
||||
/// attribute on the type or variant.
|
||||
code: Option<(TokenStream, proc_macro::Span)>,
|
||||
|
||||
/// Identifier for the binding to the `#[primary_span]` field.
|
||||
span_field: Option<(proc_macro2::Ident, proc_macro::Span)>,
|
||||
/// If a suggestion, the identifier for the binding to the `#[applicability]` field or a
|
||||
/// `rustc_errors::Applicability::*` variant directly.
|
||||
applicability: Option<(TokenStream, proc_macro::Span)>,
|
||||
|
||||
/// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error
|
||||
/// during finalization if still `false`.
|
||||
has_suggestion_parts: bool,
|
||||
}
|
||||
|
||||
impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
|
||||
@ -217,7 +212,11 @@ impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
|
||||
}
|
||||
|
||||
impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
|
||||
fn identify_kind(&mut self) -> Result<(), DiagnosticDeriveError> {
|
||||
fn identify_kind(
|
||||
&mut self,
|
||||
) -> Result<Option<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
|
||||
let mut kind_slug = None;
|
||||
|
||||
for attr in self.variant.ast().attrs {
|
||||
let span = attr.span().unwrap();
|
||||
|
||||
@ -225,116 +224,121 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
|
||||
let name = name.as_str();
|
||||
|
||||
let meta = attr.parse_meta()?;
|
||||
let kind = match meta {
|
||||
Meta::List(MetaList { ref nested, .. }) => {
|
||||
let mut nested_iter = nested.into_iter();
|
||||
if let Some(nested_attr) = nested_iter.next() {
|
||||
match nested_attr {
|
||||
NestedMeta::Meta(Meta::Path(path)) => {
|
||||
self.slug.set_once((path.clone(), span));
|
||||
}
|
||||
NestedMeta::Meta(meta @ Meta::NameValue(_))
|
||||
if matches!(
|
||||
meta.path().segments.last().unwrap().ident.to_string().as_str(),
|
||||
"code" | "applicability"
|
||||
) =>
|
||||
{
|
||||
// don't error for valid follow-up attributes
|
||||
}
|
||||
nested_attr => {
|
||||
throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
diag.help(
|
||||
"first argument of the attribute should be the diagnostic \
|
||||
slug",
|
||||
)
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for nested_attr in nested_iter {
|
||||
let meta = match nested_attr {
|
||||
NestedMeta::Meta(ref meta) => meta,
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr),
|
||||
};
|
||||
|
||||
let span = meta.span().unwrap();
|
||||
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
|
||||
let nested_name = nested_name.as_str();
|
||||
|
||||
match meta {
|
||||
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
|
||||
match nested_name {
|
||||
"code" => {
|
||||
let formatted_str = self.build_format(&s.value(), s.span());
|
||||
self.code.set_once((formatted_str, span));
|
||||
}
|
||||
"applicability" => {
|
||||
let value = match Applicability::from_str(&s.value()) {
|
||||
Ok(v) => v,
|
||||
Err(()) => {
|
||||
span_err(span, "invalid applicability").emit();
|
||||
Applicability::Unspecified
|
||||
}
|
||||
};
|
||||
self.applicability.set_once((quote! { #value }, span));
|
||||
}
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
diag.help(
|
||||
"only `code` and `applicability` are valid nested \
|
||||
attributes",
|
||||
)
|
||||
}),
|
||||
}
|
||||
}
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
if matches!(meta, Meta::Path(_)) {
|
||||
diag.help(
|
||||
"a diagnostic slug must be the first argument to the \
|
||||
attribute",
|
||||
)
|
||||
} else {
|
||||
diag
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
let Ok(kind) = SubdiagnosticKind::from_str(name) else {
|
||||
throw_invalid_attr!(attr, &meta)
|
||||
};
|
||||
|
||||
kind
|
||||
}
|
||||
_ => throw_invalid_attr!(attr, &meta),
|
||||
let Meta::List(MetaList { ref nested, .. }) = meta else {
|
||||
throw_invalid_attr!(attr, &meta);
|
||||
};
|
||||
|
||||
if matches!(
|
||||
kind,
|
||||
SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note
|
||||
) && self.code.is_some()
|
||||
{
|
||||
throw_span_err!(
|
||||
span,
|
||||
&format!("`code` is not a valid nested attribute of a `{}` attribute", name)
|
||||
);
|
||||
let mut kind = match name {
|
||||
"label" => SubdiagnosticKind::Label,
|
||||
"note" => SubdiagnosticKind::Note,
|
||||
"help" => SubdiagnosticKind::Help,
|
||||
"warning" => SubdiagnosticKind::Warn,
|
||||
_ => {
|
||||
if let Some(suggestion_kind) =
|
||||
name.strip_prefix("suggestion").and_then(|s| s.parse().ok())
|
||||
{
|
||||
SubdiagnosticKind::Suggestion { suggestion_kind, code: TokenStream::new() }
|
||||
} else if let Some(suggestion_kind) =
|
||||
name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok())
|
||||
{
|
||||
SubdiagnosticKind::MultipartSuggestion { suggestion_kind }
|
||||
} else {
|
||||
throw_invalid_attr!(attr, &meta);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut slug = None;
|
||||
let mut code = None;
|
||||
|
||||
let mut nested_iter = nested.into_iter();
|
||||
if let Some(nested_attr) = nested_iter.next() {
|
||||
match nested_attr {
|
||||
NestedMeta::Meta(Meta::Path(path)) => {
|
||||
slug.set_once((path.clone(), span));
|
||||
}
|
||||
NestedMeta::Meta(meta @ Meta::NameValue(_))
|
||||
if matches!(
|
||||
meta.path().segments.last().unwrap().ident.to_string().as_str(),
|
||||
"code" | "applicability"
|
||||
) =>
|
||||
{
|
||||
// Don't error for valid follow-up attributes.
|
||||
}
|
||||
nested_attr => {
|
||||
throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
diag.help(
|
||||
"first argument of the attribute should be the diagnostic \
|
||||
slug",
|
||||
)
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if matches!(
|
||||
kind,
|
||||
SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note
|
||||
) && self.applicability.is_some()
|
||||
{
|
||||
throw_span_err!(
|
||||
span,
|
||||
&format!(
|
||||
"`applicability` is not a valid nested attribute of a `{}` attribute",
|
||||
name
|
||||
)
|
||||
);
|
||||
for nested_attr in nested_iter {
|
||||
let meta = match nested_attr {
|
||||
NestedMeta::Meta(ref meta) => meta,
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr),
|
||||
};
|
||||
|
||||
let span = meta.span().unwrap();
|
||||
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
|
||||
let nested_name = nested_name.as_str();
|
||||
|
||||
let value = match meta {
|
||||
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => value,
|
||||
Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
diag.help("a diagnostic slug must be the first argument to the attribute")
|
||||
}),
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr),
|
||||
};
|
||||
|
||||
match nested_name {
|
||||
"code" => {
|
||||
if matches!(kind, SubdiagnosticKind::Suggestion { .. }) {
|
||||
let formatted_str = self.build_format(&value.value(), value.span());
|
||||
code.set_once((formatted_str, span));
|
||||
} else {
|
||||
span_err(
|
||||
span,
|
||||
&format!(
|
||||
"`code` is not a valid nested attribute of a `{}` attribute",
|
||||
name
|
||||
),
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
"applicability" => {
|
||||
if matches!(
|
||||
kind,
|
||||
SubdiagnosticKind::Suggestion { .. }
|
||||
| SubdiagnosticKind::MultipartSuggestion { .. }
|
||||
) {
|
||||
let value =
|
||||
Applicability::from_str(&value.value()).unwrap_or_else(|()| {
|
||||
span_err(span, "invalid applicability").emit();
|
||||
Applicability::Unspecified
|
||||
});
|
||||
self.applicability.set_once((quote! { #value }, span));
|
||||
} else {
|
||||
span_err(
|
||||
span,
|
||||
&format!(
|
||||
"`applicability` is not a valid nested attribute of a `{}` attribute",
|
||||
name
|
||||
)
|
||||
).emit();
|
||||
}
|
||||
}
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
diag.help("only `code` and `applicability` are valid nested attributes")
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
if self.slug.is_none() {
|
||||
let Some((slug, _)) = slug else {
|
||||
throw_span_err!(
|
||||
span,
|
||||
&format!(
|
||||
@ -342,150 +346,338 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
|
||||
name
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
match kind {
|
||||
SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => {
|
||||
let Some((code, _)) = code else {
|
||||
throw_span_err!(span, "suggestion without `code = \"...\"`");
|
||||
};
|
||||
*code_field = code;
|
||||
}
|
||||
SubdiagnosticKind::Label
|
||||
| SubdiagnosticKind::Note
|
||||
| SubdiagnosticKind::Help
|
||||
| SubdiagnosticKind::Warn
|
||||
| SubdiagnosticKind::MultipartSuggestion { .. } => {}
|
||||
}
|
||||
|
||||
self.kind.set_once((kind, span));
|
||||
kind_slug.set_once(((kind, slug), span))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(kind_slug.map(|(kind_slug, _)| kind_slug))
|
||||
}
|
||||
|
||||
fn generate_field_code(
|
||||
&mut self,
|
||||
binding: &BindingInfo<'_>,
|
||||
is_suggestion: bool,
|
||||
) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||
/// Generates the code for a field with no attributes.
|
||||
fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream {
|
||||
let ast = binding.ast();
|
||||
|
||||
let inner_ty = FieldInnerTy::from_type(&ast.ty);
|
||||
let info = FieldInfo {
|
||||
binding: binding,
|
||||
ty: inner_ty.inner_type().unwrap_or(&ast.ty),
|
||||
span: &ast.span(),
|
||||
};
|
||||
|
||||
for attr in &ast.attrs {
|
||||
let name = attr.path.segments.last().unwrap().ident.to_string();
|
||||
let name = name.as_str();
|
||||
let span = attr.span().unwrap();
|
||||
|
||||
let meta = attr.parse_meta()?;
|
||||
match meta {
|
||||
Meta::Path(_) => match name {
|
||||
"primary_span" => {
|
||||
report_error_if_not_applied_to_span(attr, &info)?;
|
||||
self.span_field.set_once((binding.binding.clone(), span));
|
||||
return Ok(quote! {});
|
||||
}
|
||||
"applicability" if is_suggestion => {
|
||||
report_error_if_not_applied_to_applicability(attr, &info)?;
|
||||
let binding = binding.binding.clone();
|
||||
self.applicability.set_once((quote! { #binding }, span));
|
||||
return Ok(quote! {});
|
||||
}
|
||||
"applicability" => {
|
||||
span_err(span, "`#[applicability]` is only valid on suggestions").emit();
|
||||
return Ok(quote! {});
|
||||
}
|
||||
"skip_arg" => {
|
||||
return Ok(quote! {});
|
||||
}
|
||||
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
||||
diag.help(
|
||||
"only `primary_span`, `applicability` and `skip_arg` are valid field \
|
||||
attributes",
|
||||
)
|
||||
}),
|
||||
},
|
||||
_ => throw_invalid_attr!(attr, &meta),
|
||||
}
|
||||
}
|
||||
|
||||
let ident = ast.ident.as_ref().unwrap();
|
||||
assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg");
|
||||
|
||||
let diag = &self.diag;
|
||||
let generated = quote! {
|
||||
let ident = ast.ident.as_ref().unwrap();
|
||||
quote! {
|
||||
#diag.set_arg(
|
||||
stringify!(#ident),
|
||||
#binding
|
||||
);
|
||||
};
|
||||
|
||||
Ok(inner_ty.with(binding, generated))
|
||||
}
|
||||
}
|
||||
|
||||
fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||
self.identify_kind()?;
|
||||
let Some(kind) = self.kind.map(|(kind, _)| kind) else {
|
||||
/// Generates the necessary code for all attributes on a field.
|
||||
fn generate_field_attr_code(
|
||||
&mut self,
|
||||
binding: &BindingInfo<'_>,
|
||||
kind: &SubdiagnosticKind,
|
||||
) -> TokenStream {
|
||||
let ast = binding.ast();
|
||||
assert!(ast.attrs.len() > 0, "field without attributes generating attr code");
|
||||
|
||||
// Abstract over `Vec<T>` and `Option<T>` fields using `FieldInnerTy`, which will
|
||||
// apply the generated code on each element in the `Vec` or `Option`.
|
||||
let inner_ty = FieldInnerTy::from_type(&ast.ty);
|
||||
ast.attrs
|
||||
.iter()
|
||||
.map(|attr| {
|
||||
let info = FieldInfo {
|
||||
binding,
|
||||
ty: inner_ty.inner_type().unwrap_or(&ast.ty),
|
||||
span: &ast.span(),
|
||||
};
|
||||
|
||||
let generated = self
|
||||
.generate_field_code_inner(kind, attr, info)
|
||||
.unwrap_or_else(|v| v.to_compile_error());
|
||||
|
||||
inner_ty.with(binding, generated)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generate_field_code_inner(
|
||||
&mut self,
|
||||
kind: &SubdiagnosticKind,
|
||||
attr: &Attribute,
|
||||
info: FieldInfo<'_>,
|
||||
) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||
let meta = attr.parse_meta()?;
|
||||
match meta {
|
||||
Meta::Path(path) => self.generate_field_code_inner_path(kind, attr, info, path),
|
||||
Meta::List(list @ MetaList { .. }) => {
|
||||
self.generate_field_code_inner_list(kind, attr, info, list)
|
||||
}
|
||||
_ => throw_invalid_attr!(attr, &meta),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the code for a `[Meta::Path]`-like attribute on a field (e.g. `#[primary_span]`).
|
||||
fn generate_field_code_inner_path(
|
||||
&mut self,
|
||||
kind: &SubdiagnosticKind,
|
||||
attr: &Attribute,
|
||||
info: FieldInfo<'_>,
|
||||
path: Path,
|
||||
) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||
let span = attr.span().unwrap();
|
||||
let ident = &path.segments.last().unwrap().ident;
|
||||
let name = ident.to_string();
|
||||
let name = name.as_str();
|
||||
|
||||
match name {
|
||||
"skip_arg" => Ok(quote! {}),
|
||||
"primary_span" => {
|
||||
if matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) {
|
||||
throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
|
||||
diag.help(
|
||||
"multipart suggestions use one or more `#[suggestion_part]`s rather \
|
||||
than one `#[primary_span]`",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
report_error_if_not_applied_to_span(attr, &info)?;
|
||||
|
||||
let binding = info.binding.binding.clone();
|
||||
self.span_field.set_once((binding, span));
|
||||
|
||||
Ok(quote! {})
|
||||
}
|
||||
"suggestion_part" => {
|
||||
self.has_suggestion_parts = true;
|
||||
|
||||
match kind {
|
||||
SubdiagnosticKind::MultipartSuggestion { .. } => {
|
||||
span_err(
|
||||
span,
|
||||
"`#[suggestion_part(...)]` attribute without `code = \"...\"`",
|
||||
)
|
||||
.emit();
|
||||
Ok(quote! {})
|
||||
}
|
||||
SubdiagnosticKind::Label
|
||||
| SubdiagnosticKind::Note
|
||||
| SubdiagnosticKind::Help
|
||||
| SubdiagnosticKind::Warn
|
||||
| SubdiagnosticKind::Suggestion { .. } => {
|
||||
throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
|
||||
diag.help(
|
||||
"`#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead",
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
"applicability" => {
|
||||
if let SubdiagnosticKind::Suggestion { .. }
|
||||
| SubdiagnosticKind::MultipartSuggestion { .. } = kind
|
||||
{
|
||||
report_error_if_not_applied_to_applicability(attr, &info)?;
|
||||
|
||||
let binding = info.binding.binding.clone();
|
||||
self.applicability.set_once((quote! { #binding }, span));
|
||||
} else {
|
||||
span_err(span, "`#[applicability]` is only valid on suggestions").emit();
|
||||
}
|
||||
|
||||
Ok(quote! {})
|
||||
}
|
||||
_ => throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
|
||||
let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind {
|
||||
"suggestion_part"
|
||||
} else {
|
||||
"primary_span"
|
||||
};
|
||||
diag.help(format!(
|
||||
"only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes",
|
||||
))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the code for a `[Meta::List]`-like attribute on a field (e.g.
|
||||
/// `#[suggestion_part(code = "...")]`).
|
||||
fn generate_field_code_inner_list(
|
||||
&mut self,
|
||||
kind: &SubdiagnosticKind,
|
||||
attr: &Attribute,
|
||||
info: FieldInfo<'_>,
|
||||
list: MetaList,
|
||||
) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||
let span = attr.span().unwrap();
|
||||
let ident = &list.path.segments.last().unwrap().ident;
|
||||
let name = ident.to_string();
|
||||
let name = name.as_str();
|
||||
|
||||
match name {
|
||||
"suggestion_part" => {
|
||||
if !matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) {
|
||||
throw_invalid_attr!(attr, &Meta::List(list), |diag| {
|
||||
diag.help(
|
||||
"`#[suggestion_part(...)]` is only valid in multipart suggestions",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
self.has_suggestion_parts = true;
|
||||
|
||||
report_error_if_not_applied_to_span(attr, &info)?;
|
||||
|
||||
let mut code = None;
|
||||
for nested_attr in list.nested.iter() {
|
||||
let NestedMeta::Meta(ref meta) = nested_attr else {
|
||||
throw_invalid_nested_attr!(attr, &nested_attr);
|
||||
};
|
||||
|
||||
let span = meta.span().unwrap();
|
||||
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
|
||||
let nested_name = nested_name.as_str();
|
||||
|
||||
let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) = meta else {
|
||||
throw_invalid_nested_attr!(attr, &nested_attr);
|
||||
};
|
||||
|
||||
match nested_name {
|
||||
"code" => {
|
||||
let formatted_str = self.build_format(&value.value(), value.span());
|
||||
code.set_once((formatted_str, span));
|
||||
}
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
diag.help("`code` is the only valid nested attribute")
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
let Some((code, _)) = code else {
|
||||
span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`")
|
||||
.emit();
|
||||
return Ok(quote! {});
|
||||
};
|
||||
let binding = info.binding;
|
||||
|
||||
Ok(quote! { suggestions.push((#binding, #code)); })
|
||||
}
|
||||
_ => throw_invalid_attr!(attr, &Meta::List(list), |diag| {
|
||||
let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind {
|
||||
"suggestion_part"
|
||||
} else {
|
||||
"primary_span"
|
||||
};
|
||||
diag.help(format!(
|
||||
"only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes",
|
||||
))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||
let Some((kind, slug)) = self.identify_kind()? else {
|
||||
throw_span_err!(
|
||||
self.variant.ast().ident.span().unwrap(),
|
||||
"subdiagnostic kind not specified"
|
||||
);
|
||||
};
|
||||
|
||||
let is_suggestion = matches!(kind, SubdiagnosticKind::Suggestion(_));
|
||||
|
||||
let mut args = TokenStream::new();
|
||||
for binding in self.variant.bindings() {
|
||||
let arg = self
|
||||
.generate_field_code(binding, is_suggestion)
|
||||
.unwrap_or_else(|v| v.to_compile_error());
|
||||
args.extend(arg);
|
||||
}
|
||||
|
||||
// Missing slug errors will already have been reported.
|
||||
let slug = self
|
||||
.slug
|
||||
.as_ref()
|
||||
.map(|(slug, _)| slug.clone())
|
||||
.unwrap_or_else(|| parse_quote! { you::need::to::specify::a::slug });
|
||||
let code = match self.code.as_ref() {
|
||||
Some((code, _)) => Some(quote! { #code }),
|
||||
None if is_suggestion => {
|
||||
span_err(self.span, "suggestion without `code = \"...\"`").emit();
|
||||
Some(quote! { /* macro error */ "..." })
|
||||
let init = match &kind {
|
||||
SubdiagnosticKind::Label
|
||||
| SubdiagnosticKind::Note
|
||||
| SubdiagnosticKind::Help
|
||||
| SubdiagnosticKind::Warn
|
||||
| SubdiagnosticKind::Suggestion { .. } => quote! {},
|
||||
SubdiagnosticKind::MultipartSuggestion { .. } => {
|
||||
quote! { let mut suggestions = Vec::new(); }
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let attr_args: TokenStream = self
|
||||
.variant
|
||||
.bindings()
|
||||
.iter()
|
||||
.filter(|binding| !binding.ast().attrs.is_empty())
|
||||
.map(|binding| self.generate_field_attr_code(binding, &kind))
|
||||
.collect();
|
||||
|
||||
let span_field = self.span_field.as_ref().map(|(span, _)| span);
|
||||
let applicability = match self.applicability.clone() {
|
||||
Some((applicability, _)) => Some(applicability),
|
||||
None if is_suggestion => {
|
||||
span_err(self.span, "suggestion without `applicability`").emit();
|
||||
Some(quote! { rustc_errors::Applicability::Unspecified })
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let applicability = self.applicability.take().map_or_else(
|
||||
|| quote! { rustc_errors::Applicability::Unspecified },
|
||||
|(applicability, _)| applicability,
|
||||
);
|
||||
|
||||
let diag = &self.diag;
|
||||
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
|
||||
let message = quote! { rustc_errors::fluent::#slug };
|
||||
let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) {
|
||||
if let Some(span) = span_field {
|
||||
quote! { #diag.#name(#span, #message, #code, #applicability); }
|
||||
} else {
|
||||
span_err(self.span, "suggestion without `#[primary_span]` field").emit();
|
||||
quote! { unreachable!(); }
|
||||
let call = match kind {
|
||||
SubdiagnosticKind::Suggestion { suggestion_kind, code } => {
|
||||
if let Some(span) = span_field {
|
||||
let style = suggestion_kind.to_suggestion_style();
|
||||
|
||||
quote! { #diag.#name(#span, #message, #code, #applicability, #style); }
|
||||
} else {
|
||||
span_err(self.span, "suggestion without `#[primary_span]` field").emit();
|
||||
quote! { unreachable!(); }
|
||||
}
|
||||
}
|
||||
} else if matches!(kind, SubdiagnosticKind::Label) {
|
||||
if let Some(span) = span_field {
|
||||
quote! { #diag.#name(#span, #message); }
|
||||
} else {
|
||||
span_err(self.span, "label without `#[primary_span]` field").emit();
|
||||
quote! { unreachable!(); }
|
||||
SubdiagnosticKind::MultipartSuggestion { suggestion_kind } => {
|
||||
if !self.has_suggestion_parts {
|
||||
span_err(
|
||||
self.span,
|
||||
"multipart suggestion without any `#[suggestion_part(...)]` fields",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
let style = suggestion_kind.to_suggestion_style();
|
||||
|
||||
quote! { #diag.#name(#message, suggestions, #applicability, #style); }
|
||||
}
|
||||
} else {
|
||||
if let Some(span) = span_field {
|
||||
quote! { #diag.#name(#span, #message); }
|
||||
} else {
|
||||
quote! { #diag.#name(#message); }
|
||||
SubdiagnosticKind::Label => {
|
||||
if let Some(span) = span_field {
|
||||
quote! { #diag.#name(#span, #message); }
|
||||
} else {
|
||||
span_err(self.span, "label without `#[primary_span]` field").emit();
|
||||
quote! { unreachable!(); }
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if let Some(span) = span_field {
|
||||
quote! { #diag.#name(#span, #message); }
|
||||
} else {
|
||||
quote! { #diag.#name(#message); }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let plain_args: TokenStream = self
|
||||
.variant
|
||||
.bindings()
|
||||
.iter()
|
||||
.filter(|binding| binding.ast().attrs.is_empty())
|
||||
.map(|binding| self.generate_field_set_arg(binding))
|
||||
.collect();
|
||||
|
||||
Ok(quote! {
|
||||
#init
|
||||
#attr_args
|
||||
#call
|
||||
#args
|
||||
#plain_args
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -171,8 +171,13 @@ decl_derive!(
|
||||
suggestion_short,
|
||||
suggestion_hidden,
|
||||
suggestion_verbose,
|
||||
multipart_suggestion,
|
||||
multipart_suggestion_short,
|
||||
multipart_suggestion_hidden,
|
||||
multipart_suggestion_verbose,
|
||||
// field attributes
|
||||
skip_arg,
|
||||
primary_span,
|
||||
suggestion_part,
|
||||
applicability)] => diagnostics::session_subdiagnostic_derive
|
||||
);
|
||||
|
@ -844,6 +844,12 @@ impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for Vec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for &[T] {
|
||||
fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
|
||||
self.iter().try_for_each(|t| t.visit_with(visitor))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> {
|
||||
fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
self.try_map_id(|t| t.try_fold_with(folder))
|
||||
|
@ -944,9 +944,11 @@ supported_targets! {
|
||||
|
||||
("aarch64-unknown-openbsd", aarch64_unknown_openbsd),
|
||||
("i686-unknown-openbsd", i686_unknown_openbsd),
|
||||
("powerpc-unknown-openbsd", powerpc_unknown_openbsd),
|
||||
("powerpc64-unknown-openbsd", powerpc64_unknown_openbsd),
|
||||
("riscv64gc-unknown-openbsd", riscv64gc_unknown_openbsd),
|
||||
("sparc64-unknown-openbsd", sparc64_unknown_openbsd),
|
||||
("x86_64-unknown-openbsd", x86_64_unknown_openbsd),
|
||||
("powerpc-unknown-openbsd", powerpc_unknown_openbsd),
|
||||
|
||||
("aarch64-unknown-netbsd", aarch64_unknown_netbsd),
|
||||
("armv6-unknown-netbsd-eabihf", armv6_unknown_netbsd_eabihf),
|
||||
|
17
compiler/rustc_target/src/spec/powerpc64_unknown_openbsd.rs
Normal file
17
compiler/rustc_target/src/spec/powerpc64_unknown_openbsd.rs
Normal file
@ -0,0 +1,17 @@
|
||||
use crate::abi::Endian;
|
||||
use crate::spec::{LinkerFlavor, Target, TargetOptions};
|
||||
|
||||
pub fn target() -> Target {
|
||||
let mut base = super::openbsd_base::opts();
|
||||
base.cpu = "ppc64".into();
|
||||
base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
|
||||
base.max_atomic_width = Some(64);
|
||||
|
||||
Target {
|
||||
llvm_target: "powerpc64-unknown-openbsd".into(),
|
||||
pointer_width: 64,
|
||||
data_layout: "E-m:e-i64:64-n32:64".into(),
|
||||
arch: "powerpc64".into(),
|
||||
options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base },
|
||||
}
|
||||
}
|
18
compiler/rustc_target/src/spec/riscv64gc_unknown_openbsd.rs
Normal file
18
compiler/rustc_target/src/spec/riscv64gc_unknown_openbsd.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use crate::spec::{CodeModel, Target, TargetOptions};
|
||||
|
||||
pub fn target() -> Target {
|
||||
Target {
|
||||
llvm_target: "riscv64-unknown-openbsd".into(),
|
||||
pointer_width: 64,
|
||||
data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
|
||||
arch: "riscv64".into(),
|
||||
options: TargetOptions {
|
||||
code_model: Some(CodeModel::Medium),
|
||||
cpu: "generic-rv64".into(),
|
||||
features: "+m,+a,+f,+d,+c".into(),
|
||||
llvm_abiname: "lp64d".into(),
|
||||
max_atomic_width: Some(64),
|
||||
..super::openbsd_base::opts()
|
||||
},
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@ use crate::errors::{
|
||||
};
|
||||
use crate::type_error_struct;
|
||||
|
||||
use super::suggest_call_constructor;
|
||||
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
|
||||
use rustc_ast as ast;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
@ -44,7 +43,7 @@ use rustc_middle::middle::stability;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
|
||||
use rustc_middle::ty::error::TypeError::FieldMisMatch;
|
||||
use rustc_middle::ty::subst::SubstsRef;
|
||||
use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable};
|
||||
use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::hygiene::DesugaringKind;
|
||||
use rustc_span::lev_distance::find_best_match_for_name;
|
||||
@ -2141,15 +2140,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
field: Ident,
|
||||
) -> Ty<'tcx> {
|
||||
debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
|
||||
let expr_t = self.check_expr(base);
|
||||
let expr_t = self.structurally_resolved_type(base.span, expr_t);
|
||||
let base_ty = self.check_expr(base);
|
||||
let base_ty = self.structurally_resolved_type(base.span, base_ty);
|
||||
let mut private_candidate = None;
|
||||
let mut autoderef = self.autoderef(expr.span, expr_t);
|
||||
while let Some((base_t, _)) = autoderef.next() {
|
||||
debug!("base_t: {:?}", base_t);
|
||||
match base_t.kind() {
|
||||
let mut autoderef = self.autoderef(expr.span, base_ty);
|
||||
while let Some((deref_base_ty, _)) = autoderef.next() {
|
||||
debug!("deref_base_ty: {:?}", deref_base_ty);
|
||||
match deref_base_ty.kind() {
|
||||
ty::Adt(base_def, substs) if !base_def.is_enum() => {
|
||||
debug!("struct named {:?}", base_t);
|
||||
debug!("struct named {:?}", deref_base_ty);
|
||||
let (ident, def_scope) =
|
||||
self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id);
|
||||
let fields = &base_def.non_enum_variant().fields;
|
||||
@ -2197,23 +2196,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// (#90483) apply adjustments to avoid ExprUseVisitor from
|
||||
// creating erroneous projection.
|
||||
self.apply_adjustments(base, adjustments);
|
||||
self.ban_private_field_access(expr, expr_t, field, did);
|
||||
self.ban_private_field_access(expr, base_ty, field, did);
|
||||
return field_ty;
|
||||
}
|
||||
|
||||
if field.name == kw::Empty {
|
||||
} else if self.method_exists(field, expr_t, expr.hir_id, true) {
|
||||
self.ban_take_value_of_method(expr, expr_t, field);
|
||||
} else if !expr_t.is_primitive_ty() {
|
||||
self.ban_nonexisting_field(field, base, expr, expr_t);
|
||||
} else if self.method_exists(field, base_ty, expr.hir_id, true) {
|
||||
self.ban_take_value_of_method(expr, base_ty, field);
|
||||
} else if !base_ty.is_primitive_ty() {
|
||||
self.ban_nonexisting_field(field, base, expr, base_ty);
|
||||
} else {
|
||||
let field_name = field.to_string();
|
||||
let mut err = type_error_struct!(
|
||||
self.tcx().sess,
|
||||
field.span,
|
||||
expr_t,
|
||||
base_ty,
|
||||
E0610,
|
||||
"`{expr_t}` is a primitive type and therefore doesn't have fields",
|
||||
"`{base_ty}` is a primitive type and therefore doesn't have fields",
|
||||
);
|
||||
let is_valid_suffix = |field: &str| {
|
||||
if field == "f32" || field == "f64" {
|
||||
@ -2251,7 +2250,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
None
|
||||
}
|
||||
};
|
||||
if let ty::Infer(ty::IntVar(_)) = expr_t.kind()
|
||||
if let ty::Infer(ty::IntVar(_)) = base_ty.kind()
|
||||
&& let ExprKind::Lit(Spanned {
|
||||
node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed),
|
||||
..
|
||||
@ -2280,35 +2279,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.tcx().ty_error()
|
||||
}
|
||||
|
||||
fn check_call_constructor(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
base: &'tcx hir::Expr<'tcx>,
|
||||
def_id: DefId,
|
||||
) {
|
||||
if let Some(local_id) = def_id.as_local() {
|
||||
let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id);
|
||||
let node = self.tcx.hir().get(hir_id);
|
||||
|
||||
if let Some(fields) = node.tuple_fields() {
|
||||
let kind = match self.tcx.opt_def_kind(local_id) {
|
||||
Some(DefKind::Ctor(of, _)) => of,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
suggest_call_constructor(base.span, kind, fields.len(), err);
|
||||
}
|
||||
} else {
|
||||
// The logic here isn't smart but `associated_item_def_ids`
|
||||
// doesn't work nicely on local.
|
||||
if let DefKind::Ctor(of, _) = self.tcx.def_kind(def_id) {
|
||||
let parent_def_id = self.tcx.parent(def_id);
|
||||
let fields = self.tcx.associated_item_def_ids(parent_def_id);
|
||||
suggest_call_constructor(base.span, of, fields.len(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_await_on_field_access(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
@ -2351,40 +2321,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
fn ban_nonexisting_field(
|
||||
&self,
|
||||
field: Ident,
|
||||
ident: Ident,
|
||||
base: &'tcx hir::Expr<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
expr_t: Ty<'tcx>,
|
||||
base_ty: Ty<'tcx>,
|
||||
) {
|
||||
debug!(
|
||||
"ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
|
||||
field, base, expr, expr_t
|
||||
"ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}",
|
||||
ident, base, expr, base_ty
|
||||
);
|
||||
let mut err = self.no_such_field_err(field, expr_t, base.hir_id);
|
||||
let mut err = self.no_such_field_err(ident, base_ty, base.hir_id);
|
||||
|
||||
match *expr_t.peel_refs().kind() {
|
||||
match *base_ty.peel_refs().kind() {
|
||||
ty::Array(_, len) => {
|
||||
self.maybe_suggest_array_indexing(&mut err, expr, base, field, len);
|
||||
self.maybe_suggest_array_indexing(&mut err, expr, base, ident, len);
|
||||
}
|
||||
ty::RawPtr(..) => {
|
||||
self.suggest_first_deref_field(&mut err, expr, base, field);
|
||||
self.suggest_first_deref_field(&mut err, expr, base, ident);
|
||||
}
|
||||
ty::Adt(def, _) if !def.is_enum() => {
|
||||
self.suggest_fields_on_recordish(&mut err, def, field, expr.span);
|
||||
self.suggest_fields_on_recordish(&mut err, def, ident, expr.span);
|
||||
}
|
||||
ty::Param(param_ty) => {
|
||||
self.point_at_param_definition(&mut err, param_ty);
|
||||
}
|
||||
ty::Opaque(_, _) => {
|
||||
self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs());
|
||||
}
|
||||
ty::FnDef(def_id, _) => {
|
||||
self.check_call_constructor(&mut err, base, def_id);
|
||||
self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if field.name == kw::Await {
|
||||
self.suggest_fn_call(&mut err, base, base_ty, |output_ty| {
|
||||
if let ty::Adt(def, _) = output_ty.kind() && !def.is_enum() {
|
||||
def.non_enum_variant().fields.iter().any(|field| {
|
||||
field.ident(self.tcx) == ident
|
||||
&& field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx)
|
||||
})
|
||||
} else if let ty::Tuple(tys) = output_ty.kind()
|
||||
&& let Ok(idx) = ident.as_str().parse::<usize>()
|
||||
{
|
||||
idx < tys.len()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
if ident.name == kw::Await {
|
||||
// We know by construction that `<expr>.await` is either on Rust 2015
|
||||
// or results in `ExprKind::Await`. Suggest switching the edition to 2018.
|
||||
err.note("to `.await` a `Future`, switch to Rust 2018 or later");
|
||||
|
@ -2,6 +2,7 @@ use super::FnCtxt;
|
||||
use crate::astconv::AstConv;
|
||||
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
|
||||
|
||||
use hir::def_id::DefId;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
@ -61,70 +62,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pointing_at_return_type
|
||||
}
|
||||
|
||||
/// When encountering an fn-like ctor that needs to unify with a value, check whether calling
|
||||
/// the ctor would successfully solve the type mismatch and if so, suggest it:
|
||||
/// When encountering an fn-like type, try accessing the output of the type
|
||||
/// // and suggesting calling it if it satisfies a predicate (i.e. if the
|
||||
/// output has a method or a field):
|
||||
/// ```compile_fail,E0308
|
||||
/// fn foo(x: usize) -> usize { x }
|
||||
/// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
|
||||
/// ```
|
||||
fn suggest_fn_call(
|
||||
pub(crate) fn suggest_fn_call(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
expr: &hir::Expr<'_>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
|
||||
) -> bool {
|
||||
let (def_id, output, inputs) = match *found.kind() {
|
||||
ty::FnDef(def_id, _) => {
|
||||
let fn_sig = found.fn_sig(self.tcx);
|
||||
(def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len())
|
||||
}
|
||||
ty::Closure(def_id, substs) => {
|
||||
let fn_sig = substs.as_closure().sig();
|
||||
(def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)
|
||||
}
|
||||
ty::Opaque(def_id, substs) => {
|
||||
let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
|
||||
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
|
||||
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
|
||||
// args tuple will always be substs[1]
|
||||
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
|
||||
{
|
||||
Some((
|
||||
pred.kind().rebind(proj.term.ty().unwrap()),
|
||||
args.len(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some((output, inputs)) = sig {
|
||||
(def_id, output, inputs)
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
|
||||
let output = self.normalize_associated_types_in(expr.span, output);
|
||||
if !output.is_ty_var() && self.can_coerce(output, expected) {
|
||||
let (sugg_call, mut applicability) = match inputs {
|
||||
let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found)
|
||||
else { return false; };
|
||||
if can_satisfy(output) {
|
||||
let (sugg_call, mut applicability) = match inputs.len() {
|
||||
0 => ("".to_string(), Applicability::MachineApplicable),
|
||||
1..=4 => (
|
||||
(0..inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
|
||||
Applicability::MachineApplicable,
|
||||
inputs
|
||||
.iter()
|
||||
.map(|ty| {
|
||||
if ty.is_suggestable(self.tcx, false) {
|
||||
format!("/* {ty} */")
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
Applicability::HasPlaceholders,
|
||||
),
|
||||
_ => ("...".to_string(), Applicability::HasPlaceholders),
|
||||
_ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
|
||||
};
|
||||
|
||||
let msg = match self.tcx.def_kind(def_id) {
|
||||
DefKind::Fn => "call this function",
|
||||
DefKind::Closure | DefKind::OpaqueTy => "call this closure",
|
||||
DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct",
|
||||
DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant",
|
||||
_ => "call this function",
|
||||
let msg = match def_id_or_name {
|
||||
DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
|
||||
DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(),
|
||||
DefKind::Ctor(CtorOf::Variant, _) => {
|
||||
"instantiate this tuple variant".to_string()
|
||||
}
|
||||
kind => format!("call this {}", kind.descr(def_id)),
|
||||
},
|
||||
DefIdOrName::Name(name) => format!("call this {name}"),
|
||||
};
|
||||
|
||||
let sugg = match expr.kind {
|
||||
@ -161,6 +143,179 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
false
|
||||
}
|
||||
|
||||
fn extract_callable_info(
|
||||
&self,
|
||||
expr: &Expr<'_>,
|
||||
found: Ty<'tcx>,
|
||||
) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
|
||||
// Autoderef is useful here because sometimes we box callables, etc.
|
||||
let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
|
||||
match *found.kind() {
|
||||
ty::FnPtr(fn_sig) =>
|
||||
Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())),
|
||||
ty::FnDef(def_id, _) => {
|
||||
let fn_sig = found.fn_sig(self.tcx);
|
||||
Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
|
||||
}
|
||||
ty::Closure(def_id, substs) => {
|
||||
let fn_sig = substs.as_closure().sig();
|
||||
Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..])))
|
||||
}
|
||||
ty::Opaque(def_id, substs) => {
|
||||
self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
|
||||
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
|
||||
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
|
||||
// args tuple will always be substs[1]
|
||||
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
|
||||
{
|
||||
Some((
|
||||
DefIdOrName::DefId(def_id),
|
||||
pred.kind().rebind(proj.term.ty().unwrap()),
|
||||
pred.kind().rebind(args.as_slice()),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
ty::Dynamic(data, _) => {
|
||||
data.iter().find_map(|pred| {
|
||||
if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
|
||||
&& Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output()
|
||||
// for existential projection, substs are shifted over by 1
|
||||
&& let ty::Tuple(args) = proj.substs.type_at(0).kind()
|
||||
{
|
||||
Some((
|
||||
DefIdOrName::Name("trait object"),
|
||||
pred.rebind(proj.term.ty().unwrap()),
|
||||
pred.rebind(args.as_slice()),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
ty::Param(param) => {
|
||||
let def_id = self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx).def_id;
|
||||
self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| {
|
||||
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
|
||||
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
|
||||
&& proj.projection_ty.self_ty() == found
|
||||
// args tuple will always be substs[1]
|
||||
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
|
||||
{
|
||||
Some((
|
||||
DefIdOrName::DefId(def_id),
|
||||
pred.kind().rebind(proj.term.ty().unwrap()),
|
||||
pred.kind().rebind(args.as_slice()),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}) else { return None; };
|
||||
|
||||
let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
|
||||
let inputs = inputs
|
||||
.skip_binder()
|
||||
.iter()
|
||||
.map(|ty| {
|
||||
self.replace_bound_vars_with_fresh_vars(
|
||||
expr.span,
|
||||
infer::FnCall,
|
||||
inputs.rebind(*ty),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// We don't want to register any extra obligations, which should be
|
||||
// implied by wf, but also because that would possibly result in
|
||||
// erroneous errors later on.
|
||||
let infer::InferOk { value: output, obligations: _ } =
|
||||
self.normalize_associated_types_in_as_infer_ok(expr.span, output);
|
||||
|
||||
if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
|
||||
}
|
||||
|
||||
pub fn suggest_two_fn_call(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
lhs_expr: &'tcx hir::Expr<'tcx>,
|
||||
lhs_ty: Ty<'tcx>,
|
||||
rhs_expr: &'tcx hir::Expr<'tcx>,
|
||||
rhs_ty: Ty<'tcx>,
|
||||
can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
|
||||
) -> bool {
|
||||
let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty)
|
||||
else { return false; };
|
||||
let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
|
||||
else { return false; };
|
||||
|
||||
if can_satisfy(lhs_output_ty, rhs_output_ty) {
|
||||
let mut sugg = vec![];
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] {
|
||||
let (sugg_call, this_applicability) = match inputs.len() {
|
||||
0 => ("".to_string(), Applicability::MachineApplicable),
|
||||
1..=4 => (
|
||||
inputs
|
||||
.iter()
|
||||
.map(|ty| {
|
||||
if ty.is_suggestable(self.tcx, false) {
|
||||
format!("/* {ty} */")
|
||||
} else {
|
||||
"/* value */".to_string()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
Applicability::HasPlaceholders,
|
||||
),
|
||||
_ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
|
||||
};
|
||||
|
||||
applicability = applicability.max(this_applicability);
|
||||
|
||||
match expr.kind {
|
||||
hir::ExprKind::Call(..)
|
||||
| hir::ExprKind::Path(..)
|
||||
| hir::ExprKind::Index(..)
|
||||
| hir::ExprKind::Lit(..) => {
|
||||
sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]);
|
||||
}
|
||||
hir::ExprKind::Closure { .. } => {
|
||||
// Might be `{ expr } || { bool }`
|
||||
applicability = Applicability::MaybeIncorrect;
|
||||
sugg.extend([
|
||||
(expr.span.shrink_to_lo(), "(".to_string()),
|
||||
(expr.span.shrink_to_hi(), format!(")({sugg_call})")),
|
||||
]);
|
||||
}
|
||||
_ => {
|
||||
sugg.extend([
|
||||
(expr.span.shrink_to_lo(), "(".to_string()),
|
||||
(expr.span.shrink_to_hi(), format!(")({sugg_call})")),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err.multipart_suggestion_verbose(
|
||||
format!("use parentheses to call these"),
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn suggest_deref_ref_or_into(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
@ -178,12 +333,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
} else {
|
||||
err.span_suggestion(sp, &msg, suggestion, applicability);
|
||||
}
|
||||
} else if let (ty::FnDef(def_id, ..), true) =
|
||||
(&found.kind(), self.suggest_fn_call(err, expr, expected, found))
|
||||
} else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
|
||||
&& let ty::FnDef(def_id, ..) = &found.kind()
|
||||
&& let Some(sp) = self.tcx.hir().span_if_local(*def_id)
|
||||
{
|
||||
if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
|
||||
err.span_label(sp, format!("{found} defined here"));
|
||||
}
|
||||
err.span_label(sp, format!("{found} defined here"));
|
||||
} else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
|
||||
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
|
||||
if !methods.is_empty() {
|
||||
@ -911,3 +1065,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum DefIdOrName {
|
||||
DefId(DefId),
|
||||
Name(&'static str),
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ use std::cmp::Ordering;
|
||||
use std::iter;
|
||||
|
||||
use super::probe::{Mode, ProbeScope};
|
||||
use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData};
|
||||
use super::{CandidateSource, MethodError, NoMatchData};
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
|
||||
@ -363,44 +363,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
);
|
||||
}
|
||||
|
||||
if self.is_fn_ty(rcvr_ty, span) {
|
||||
if let SelfSource::MethodCall(expr) = source {
|
||||
let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() {
|
||||
if let Some(local_id) = def_id.as_local() {
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(local_id);
|
||||
let node = tcx.hir().get(hir_id);
|
||||
let fields = node.tuple_fields();
|
||||
if let Some(fields) = fields
|
||||
&& let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) {
|
||||
Some((fields.len(), of))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
// The logic here isn't smart but `associated_item_def_ids`
|
||||
// doesn't work nicely on local.
|
||||
if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) {
|
||||
let parent_def_id = tcx.parent(*def_id);
|
||||
Some((tcx.associated_item_def_ids(parent_def_id).len(), of))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// If the function is a tuple constructor, we recommend that they call it
|
||||
if let Some((fields, kind)) = suggest {
|
||||
suggest_call_constructor(expr.span, kind, fields, &mut err);
|
||||
} else {
|
||||
// General case
|
||||
err.span_label(
|
||||
expr.span,
|
||||
"this is a function, perhaps you wish to call it",
|
||||
);
|
||||
}
|
||||
}
|
||||
if let SelfSource::MethodCall(rcvr_expr) = source {
|
||||
self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| {
|
||||
let call_expr = self
|
||||
.tcx
|
||||
.hir()
|
||||
.expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id));
|
||||
let probe = self.lookup_probe(
|
||||
span,
|
||||
item_name,
|
||||
output_ty,
|
||||
call_expr,
|
||||
ProbeScope::AllTraits,
|
||||
);
|
||||
probe.is_ok()
|
||||
});
|
||||
}
|
||||
|
||||
let mut custom_span_label = false;
|
||||
|
@ -96,7 +96,6 @@ use check::{check_abi, check_fn, check_mod_item_types};
|
||||
pub use diverges::Diverges;
|
||||
pub use expectation::Expectation;
|
||||
pub use fn_ctxt::*;
|
||||
use hir::def::CtorOf;
|
||||
pub use inherited::{Inherited, InheritedBuilder};
|
||||
|
||||
use crate::astconv::AstConv;
|
||||
@ -960,31 +959,3 @@ fn has_expected_num_generic_args<'tcx>(
|
||||
generics.count() == expected + if generics.has_self { 1 } else { 0 }
|
||||
})
|
||||
}
|
||||
|
||||
/// Suggests calling the constructor of a tuple struct or enum variant
|
||||
///
|
||||
/// * `snippet` - The snippet of code that references the constructor
|
||||
/// * `span` - The span of the snippet
|
||||
/// * `params` - The number of parameters the constructor accepts
|
||||
/// * `err` - A mutable diagnostic builder to add the suggestion to
|
||||
fn suggest_call_constructor(span: Span, kind: CtorOf, params: usize, err: &mut Diagnostic) {
|
||||
// Note: tuple-structs don't have named fields, so just use placeholders
|
||||
let args = vec!["_"; params].join(", ");
|
||||
let applicable = if params > 0 {
|
||||
Applicability::HasPlaceholders
|
||||
} else {
|
||||
// When n = 0, it's an empty-tuple struct/enum variant
|
||||
// so we trivially know how to construct it
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
let kind = match kind {
|
||||
CtorOf::Struct => "a struct",
|
||||
CtorOf::Variant => "an enum variant",
|
||||
};
|
||||
err.span_label(span, &format!("this is the constructor of {kind}"));
|
||||
err.multipart_suggestion(
|
||||
"call the constructor",
|
||||
vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))],
|
||||
applicable,
|
||||
);
|
||||
}
|
||||
|
@ -410,26 +410,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
};
|
||||
let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}");
|
||||
if !lhs_expr.span.eq(&rhs_expr.span) {
|
||||
self.add_type_neq_err_label(
|
||||
&mut err,
|
||||
lhs_expr.span,
|
||||
lhs_ty,
|
||||
rhs_ty,
|
||||
rhs_expr,
|
||||
op,
|
||||
is_assign,
|
||||
expected,
|
||||
);
|
||||
self.add_type_neq_err_label(
|
||||
&mut err,
|
||||
rhs_expr.span,
|
||||
rhs_ty,
|
||||
lhs_ty,
|
||||
lhs_expr,
|
||||
op,
|
||||
is_assign,
|
||||
expected,
|
||||
);
|
||||
err.span_label(lhs_expr.span, lhs_ty.to_string());
|
||||
err.span_label(rhs_expr.span, rhs_ty.to_string());
|
||||
}
|
||||
self.note_unmet_impls_on_type(&mut err, errors);
|
||||
(err, missing_trait, use_output)
|
||||
@ -468,17 +450,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
let is_compatible = |lhs_ty, rhs_ty| {
|
||||
self.lookup_op_method(
|
||||
lhs_ty,
|
||||
Some(rhs_ty),
|
||||
Some(rhs_expr),
|
||||
Op::Binary(op, is_assign),
|
||||
expected,
|
||||
)
|
||||
.is_ok()
|
||||
};
|
||||
|
||||
// We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
|
||||
// `a += b` => `*a += b` if a is a mut ref.
|
||||
if is_assign == IsAssign::Yes
|
||||
&& let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
|
||||
suggest_deref_binop(lhs_deref_ty);
|
||||
if !op.span.can_be_used_for_suggestions() {
|
||||
// Suppress suggestions when lhs and rhs are not in the same span as the error
|
||||
} else if is_assign == IsAssign::Yes
|
||||
&& let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
|
||||
{
|
||||
suggest_deref_binop(lhs_deref_ty);
|
||||
} else if is_assign == IsAssign::No
|
||||
&& let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() {
|
||||
if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) {
|
||||
&& let Ref(_, lhs_deref_ty, _) = lhs_ty.kind()
|
||||
{
|
||||
if self.type_is_copy_modulo_regions(
|
||||
self.param_env,
|
||||
*lhs_deref_ty,
|
||||
lhs_expr.span,
|
||||
) {
|
||||
suggest_deref_binop(*lhs_deref_ty);
|
||||
}
|
||||
} else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| {
|
||||
is_compatible(lhs_ty, rhs_ty)
|
||||
}) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| {
|
||||
is_compatible(lhs_ty, rhs_ty)
|
||||
}) || self.suggest_two_fn_call(
|
||||
&mut err,
|
||||
rhs_expr,
|
||||
rhs_ty,
|
||||
lhs_expr,
|
||||
lhs_ty,
|
||||
|lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty),
|
||||
) {
|
||||
// Cool
|
||||
}
|
||||
|
||||
if let Some(missing_trait) = missing_trait {
|
||||
let mut visitor = TypeParamVisitor(vec![]);
|
||||
visitor.visit_ty(lhs_ty);
|
||||
@ -548,69 +563,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
(lhs_ty, rhs_ty, return_ty)
|
||||
}
|
||||
|
||||
/// If one of the types is an uncalled function and calling it would yield the other type,
|
||||
/// suggest calling the function. Returns `true` if suggestion would apply (even if not given).
|
||||
fn add_type_neq_err_label(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
span: Span,
|
||||
ty: Ty<'tcx>,
|
||||
other_ty: Ty<'tcx>,
|
||||
other_expr: &'tcx hir::Expr<'tcx>,
|
||||
op: hir::BinOp,
|
||||
is_assign: IsAssign,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> bool /* did we suggest to call a function because of missing parentheses? */ {
|
||||
err.span_label(span, ty.to_string());
|
||||
if let FnDef(def_id, _) = *ty.kind() {
|
||||
if !self.tcx.has_typeck_results(def_id) {
|
||||
return false;
|
||||
}
|
||||
// FIXME: Instead of exiting early when encountering bound vars in
|
||||
// the function signature, consider keeping the binder here and
|
||||
// propagating it downwards.
|
||||
let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let other_ty = if let FnDef(def_id, _) = *other_ty.kind() {
|
||||
if !self.tcx.has_typeck_results(def_id) {
|
||||
return false;
|
||||
}
|
||||
// We're emitting a suggestion, so we can just ignore regions
|
||||
self.tcx.fn_sig(def_id).skip_binder().output()
|
||||
} else {
|
||||
other_ty
|
||||
};
|
||||
|
||||
if self
|
||||
.lookup_op_method(
|
||||
fn_sig.output(),
|
||||
Some(other_ty),
|
||||
Some(other_expr),
|
||||
Op::Binary(op, is_assign),
|
||||
expected,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
|
||||
("( /* arguments */ )", Applicability::HasPlaceholders)
|
||||
} else {
|
||||
("()", Applicability::MaybeIncorrect)
|
||||
};
|
||||
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_hi(),
|
||||
"you might have forgotten to call this function",
|
||||
variable_snippet,
|
||||
applicability,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Provide actionable suggestions when trying to add two strings with incorrect types,
|
||||
/// like `&str + &str`, `String + String` and `&str + &String`.
|
||||
///
|
||||
|
@ -1262,7 +1262,11 @@ fn check_impl<'tcx>(
|
||||
}
|
||||
None => {
|
||||
let self_ty = tcx.type_of(item.def_id);
|
||||
let self_ty = wfcx.normalize(item.span, None, self_ty);
|
||||
let self_ty = wfcx.normalize(
|
||||
item.span,
|
||||
Some(WellFormedLoc::Ty(item.hir_id().expect_owner())),
|
||||
self_ty,
|
||||
);
|
||||
wfcx.register_wf_obligation(
|
||||
ast_self_ty.span,
|
||||
Some(WellFormedLoc::Ty(item.hir_id().expect_owner())),
|
||||
@ -1307,7 +1311,11 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
|
||||
// parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
|
||||
// be sure if it will error or not as user might always specify the other.
|
||||
if !ty.needs_subst() {
|
||||
wfcx.register_wf_obligation(tcx.def_span(param.def_id), None, ty.into());
|
||||
wfcx.register_wf_obligation(
|
||||
tcx.def_span(param.def_id),
|
||||
Some(WellFormedLoc::Ty(param.def_id.expect_local())),
|
||||
ty.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1512,7 +1520,14 @@ fn check_fn_or_method<'tcx>(
|
||||
);
|
||||
}
|
||||
|
||||
wfcx.register_wf_obligation(hir_decl.output.span(), None, sig.output().into());
|
||||
wfcx.register_wf_obligation(
|
||||
hir_decl.output.span(),
|
||||
Some(WellFormedLoc::Param {
|
||||
function: def_id,
|
||||
param_idx: sig.inputs().len().try_into().unwrap(),
|
||||
}),
|
||||
sig.output().into(),
|
||||
);
|
||||
|
||||
check_where_clauses(wfcx, span, def_id);
|
||||
}
|
||||
|
@ -140,6 +140,10 @@ fn diagnostic_hir_wf_check<'tcx>(
|
||||
hir::Node::ForeignItem(ForeignItem {
|
||||
kind: ForeignItemKind::Static(ty, _), ..
|
||||
}) => Some(*ty),
|
||||
hir::Node::GenericParam(hir::GenericParam {
|
||||
kind: hir::GenericParamKind::Type { default: Some(ty), .. },
|
||||
..
|
||||
}) => Some(*ty),
|
||||
ref node => bug!("Unexpected node {:?}", node),
|
||||
},
|
||||
WellFormedLoc::Param { function: _, param_idx } => {
|
||||
|
@ -137,11 +137,9 @@ mod imp {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
|
||||
mod imp {
|
||||
use crate::fs::File;
|
||||
use crate::io::Read;
|
||||
use crate::sys::os::errno;
|
||||
use crate::io;
|
||||
use crate::sys::weak::weak;
|
||||
use libc::{c_int, c_void, size_t};
|
||||
|
||||
@ -155,7 +153,7 @@ mod imp {
|
||||
for s in v.chunks_mut(256) {
|
||||
let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) };
|
||||
if ret == -1 {
|
||||
panic!("unexpected getentropy error: {}", errno());
|
||||
panic!("unexpected getentropy error: {}", io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
true
|
||||
@ -163,14 +161,64 @@ mod imp {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn fallback_fill_bytes(v: &mut [u8]) {
|
||||
use crate::fs::File;
|
||||
use crate::io::Read;
|
||||
|
||||
let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
|
||||
file.read_exact(v).expect("failed to read /dev/urandom")
|
||||
}
|
||||
|
||||
// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
|
||||
// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
|
||||
// from `/dev/random` and which runs on its own thread accessed via GCD.
|
||||
//
|
||||
// This is very heavyweight compared to the alternatives, but they may not be usable:
|
||||
// - `getentropy` was added in iOS 10, but we support a minimum of iOS 7
|
||||
// - `/dev/urandom` is not accessible inside the iOS app sandbox.
|
||||
//
|
||||
// Therefore `SecRandomCopyBytes` is only used on older iOS versions where no
|
||||
// better options are present.
|
||||
#[cfg(target_os = "ios")]
|
||||
fn fallback_fill_bytes(v: &mut [u8]) {
|
||||
use crate::ptr;
|
||||
|
||||
enum SecRandom {}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
const kSecRandomDefault: *const SecRandom = ptr::null();
|
||||
|
||||
extern "C" {
|
||||
fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
|
||||
}
|
||||
|
||||
let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
|
||||
if ret == -1 {
|
||||
panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
// All supported versions of watchOS (>= 5) have support for `getentropy`.
|
||||
#[cfg(target_os = "watchos")]
|
||||
#[cold]
|
||||
fn fallback_fill_bytes(_: &mut [u8]) {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
pub fn fill_bytes(v: &mut [u8]) {
|
||||
if getentropy_fill_bytes(v) {
|
||||
return;
|
||||
}
|
||||
|
||||
// for older macos which doesn't support getentropy
|
||||
let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
|
||||
file.read_exact(v).expect("failed to read /dev/urandom")
|
||||
// Older macOS versions (< 10.12) don't support `getentropy`. Fallback to
|
||||
// reading from `/dev/urandom` on these systems.
|
||||
//
|
||||
// Older iOS versions (< 10) don't support it either. Fallback to
|
||||
// `SecRandomCopyBytes` on these systems. On watchOS, this is unreachable
|
||||
// because the minimum supported version is 5 while `getentropy` became accessible
|
||||
// in 3.
|
||||
fallback_fill_bytes(v)
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,36 +237,6 @@ mod imp {
|
||||
}
|
||||
}
|
||||
|
||||
// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
|
||||
// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
|
||||
// from `/dev/random` and which runs on its own thread accessed via GCD.
|
||||
// This seems needlessly heavyweight for the purposes of generating two u64s
|
||||
// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is
|
||||
// only used on iOS where direct access to `/dev/urandom` is blocked by the
|
||||
// sandbox.
|
||||
#[cfg(any(target_os = "ios", target_os = "watchos"))]
|
||||
mod imp {
|
||||
use crate::io;
|
||||
use crate::ptr;
|
||||
use libc::{c_int, size_t};
|
||||
|
||||
enum SecRandom {}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
const kSecRandomDefault: *const SecRandom = ptr::null();
|
||||
|
||||
extern "C" {
|
||||
fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
|
||||
}
|
||||
|
||||
pub fn fill_bytes(v: &mut [u8]) {
|
||||
let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
|
||||
if ret == -1 {
|
||||
panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
|
||||
mod imp {
|
||||
use crate::ptr;
|
||||
|
@ -228,8 +228,6 @@ pub const IPV6_ADD_MEMBERSHIP: c_int = 12;
|
||||
pub const IPV6_DROP_MEMBERSHIP: c_int = 13;
|
||||
pub const MSG_PEEK: c_int = 0x2;
|
||||
|
||||
pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = 0x800;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct linger {
|
||||
@ -503,6 +501,8 @@ pub struct FILE_END_OF_FILE_INFO {
|
||||
pub EndOfFile: LARGE_INTEGER,
|
||||
}
|
||||
|
||||
/// NB: Use carefully! In general using this as a reference is likely to get the
|
||||
/// provenance wrong for the `rest` field!
|
||||
#[repr(C)]
|
||||
pub struct REPARSE_DATA_BUFFER {
|
||||
pub ReparseTag: c_uint,
|
||||
@ -511,6 +511,8 @@ pub struct REPARSE_DATA_BUFFER {
|
||||
pub rest: (),
|
||||
}
|
||||
|
||||
/// NB: Use carefully! In general using this as a reference is likely to get the
|
||||
/// provenance wrong for the `PathBuffer` field!
|
||||
#[repr(C)]
|
||||
pub struct SYMBOLIC_LINK_REPARSE_BUFFER {
|
||||
pub SubstituteNameOffset: c_ushort,
|
||||
@ -521,6 +523,8 @@ pub struct SYMBOLIC_LINK_REPARSE_BUFFER {
|
||||
pub PathBuffer: WCHAR,
|
||||
}
|
||||
|
||||
/// NB: Use carefully! In general using this as a reference is likely to get the
|
||||
/// provenance wrong for the `PathBuffer` field!
|
||||
#[repr(C)]
|
||||
pub struct MOUNT_POINT_REPARSE_BUFFER {
|
||||
pub SubstituteNameOffset: c_ushort,
|
||||
@ -1032,7 +1036,6 @@ extern "system" {
|
||||
pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void;
|
||||
pub fn GetModuleHandleA(lpModuleName: LPCSTR) -> HMODULE;
|
||||
pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
|
||||
pub fn LoadLibraryExA(lplibfilename: *const i8, hfile: HANDLE, dwflags: u32) -> HINSTANCE;
|
||||
|
||||
pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME);
|
||||
pub fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO);
|
||||
|
@ -21,9 +21,52 @@
|
||||
|
||||
use crate::ffi::{c_void, CStr};
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sync::atomic::{AtomicBool, Ordering};
|
||||
use crate::sync::atomic::Ordering;
|
||||
use crate::sys::c;
|
||||
|
||||
// This uses a static initializer to preload some imported functions.
|
||||
// The CRT (C runtime) executes static initializers before `main`
|
||||
// is called (for binaries) and before `DllMain` is called (for DLLs).
|
||||
//
|
||||
// It works by contributing a global symbol to the `.CRT$XCT` section.
|
||||
// The linker builds a table of all static initializer functions.
|
||||
// The CRT startup code then iterates that table, calling each
|
||||
// initializer function.
|
||||
//
|
||||
// NOTE: User code should instead use .CRT$XCU to reliably run after std's initializer.
|
||||
// If you're reading this and would like a guarantee here, please
|
||||
// file an issue for discussion; currently we don't guarantee any functionality
|
||||
// before main.
|
||||
// See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170
|
||||
#[used]
|
||||
#[link_section = ".CRT$XCT"]
|
||||
static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
|
||||
|
||||
/// Preload some imported functions.
|
||||
///
|
||||
/// Note that any functions included here will be unconditionally loaded in
|
||||
/// the final binary, regardless of whether or not they're actually used.
|
||||
///
|
||||
/// Therefore, this should be limited to `compat_fn_optional` functions which
|
||||
/// must be preloaded or any functions where lazier loading demonstrates a
|
||||
/// negative performance impact in practical situations.
|
||||
///
|
||||
/// Currently we only preload `WaitOnAddress` and `WakeByAddressSingle`.
|
||||
unsafe extern "C" fn init() {
|
||||
// In an exe this code is executed before main() so is single threaded.
|
||||
// In a DLL the system's loader lock will be held thereby synchronizing
|
||||
// access. So the same best practices apply here as they do to running in DllMain:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
|
||||
//
|
||||
// DO NOT do anything interesting or complicated in this function! DO NOT call
|
||||
// any Rust functions or CRT functions if those functions touch any global state,
|
||||
// because this function runs during global initialization. For example, DO NOT
|
||||
// do any dynamic allocation, don't call LoadLibrary, etc.
|
||||
|
||||
// Attempt to preload the synch functions.
|
||||
load_synch_functions();
|
||||
}
|
||||
|
||||
/// Helper macro for creating CStrs from literals and symbol names.
|
||||
macro_rules! ansi_str {
|
||||
(sym $ident:ident) => {{
|
||||
@ -75,20 +118,6 @@ impl Module {
|
||||
NonNull::new(module).map(Self)
|
||||
}
|
||||
|
||||
/// Load the library (if not already loaded)
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The module must not be unloaded.
|
||||
pub unsafe fn load_system_library(name: &CStr) -> Option<Self> {
|
||||
let module = c::LoadLibraryExA(
|
||||
name.as_ptr(),
|
||||
crate::ptr::null_mut(),
|
||||
c::LOAD_LIBRARY_SEARCH_SYSTEM32,
|
||||
);
|
||||
NonNull::new(module).map(Self)
|
||||
}
|
||||
|
||||
// Try to get the address of a function.
|
||||
pub fn proc_address(self, name: &CStr) -> Option<NonNull<c_void>> {
|
||||
// SAFETY:
|
||||
@ -182,14 +211,10 @@ macro_rules! compat_fn_optional {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn option() -> Option<F> {
|
||||
let f = PTR.load(Ordering::Acquire);
|
||||
if !f.is_null() { Some(unsafe { mem::transmute(f) }) } else { try_load() }
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn try_load() -> Option<F> {
|
||||
$load_functions;
|
||||
NonNull::new(PTR.load(Ordering::Acquire)).map(|f| unsafe { mem::transmute(f) })
|
||||
// Miri does not understand the way we do preloading
|
||||
// therefore load the function here instead.
|
||||
#[cfg(miri)] $load_functions;
|
||||
NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) })
|
||||
}
|
||||
}
|
||||
)+
|
||||
@ -205,17 +230,14 @@ pub(super) fn load_synch_functions() {
|
||||
|
||||
// Try loading the library and all the required functions.
|
||||
// If any step fails, then they all fail.
|
||||
let library = unsafe { Module::load_system_library(MODULE_NAME) }?;
|
||||
let library = unsafe { Module::new(MODULE_NAME) }?;
|
||||
let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?;
|
||||
let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?;
|
||||
|
||||
c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Release);
|
||||
c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Release);
|
||||
c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed);
|
||||
c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed);
|
||||
Some(())
|
||||
}
|
||||
|
||||
// Try to load the module but skip loading if a previous attempt failed.
|
||||
static LOAD_MODULE: AtomicBool = AtomicBool::new(true);
|
||||
let module_loaded = LOAD_MODULE.load(Ordering::Acquire) && try_load().is_some();
|
||||
LOAD_MODULE.store(module_loaded, Ordering::Release)
|
||||
try_load();
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ use crate::slice;
|
||||
use crate::sync::Arc;
|
||||
use crate::sys::handle::Handle;
|
||||
use crate::sys::time::SystemTime;
|
||||
use crate::sys::{c, cvt};
|
||||
use crate::sys::{c, cvt, Align8};
|
||||
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
||||
use crate::thread;
|
||||
|
||||
@ -326,9 +326,9 @@ impl File {
|
||||
cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
|
||||
let mut reparse_tag = 0;
|
||||
if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
|
||||
let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
||||
let mut b = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
|
||||
if let Ok((_, buf)) = self.reparse_point(&mut b) {
|
||||
reparse_tag = buf.ReparseTag;
|
||||
reparse_tag = (*buf).ReparseTag;
|
||||
}
|
||||
}
|
||||
Ok(FileAttr {
|
||||
@ -389,9 +389,9 @@ impl File {
|
||||
attr.file_size = info.AllocationSize as u64;
|
||||
attr.number_of_links = Some(info.NumberOfLinks);
|
||||
if attr.file_type().is_reparse_point() {
|
||||
let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
||||
let mut b = Align8([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
|
||||
if let Ok((_, buf)) = self.reparse_point(&mut b) {
|
||||
attr.reparse_tag = buf.ReparseTag;
|
||||
attr.reparse_tag = (*buf).ReparseTag;
|
||||
}
|
||||
}
|
||||
Ok(attr)
|
||||
@ -458,38 +458,46 @@ impl File {
|
||||
Ok(Self { handle: self.handle.try_clone()? })
|
||||
}
|
||||
|
||||
fn reparse_point<'a>(
|
||||
// NB: returned pointer is derived from `space`, and has provenance to
|
||||
// match. A raw pointer is returned rather than a reference in order to
|
||||
// avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
|
||||
fn reparse_point(
|
||||
&self,
|
||||
space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE],
|
||||
) -> io::Result<(c::DWORD, &'a c::REPARSE_DATA_BUFFER)> {
|
||||
space: &mut Align8<[u8]>,
|
||||
) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
|
||||
unsafe {
|
||||
let mut bytes = 0;
|
||||
cvt({
|
||||
// Grab this in advance to avoid it invalidating the pointer
|
||||
// we get from `space.0.as_mut_ptr()`.
|
||||
let len = space.0.len();
|
||||
c::DeviceIoControl(
|
||||
self.handle.as_raw_handle(),
|
||||
c::FSCTL_GET_REPARSE_POINT,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
space.as_mut_ptr() as *mut _,
|
||||
space.len() as c::DWORD,
|
||||
space.0.as_mut_ptr().cast(),
|
||||
len as c::DWORD,
|
||||
&mut bytes,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
})?;
|
||||
Ok((bytes, &*(space.as_ptr() as *const c::REPARSE_DATA_BUFFER)))
|
||||
const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
|
||||
Ok((bytes, space.0.as_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
|
||||
}
|
||||
}
|
||||
|
||||
fn readlink(&self) -> io::Result<PathBuf> {
|
||||
let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
||||
let mut space = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
|
||||
let (_bytes, buf) = self.reparse_point(&mut space)?;
|
||||
unsafe {
|
||||
let (path_buffer, subst_off, subst_len, relative) = match buf.ReparseTag {
|
||||
let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
|
||||
c::IO_REPARSE_TAG_SYMLINK => {
|
||||
let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
|
||||
&buf.rest as *const _ as *const _;
|
||||
ptr::addr_of!((*buf).rest).cast();
|
||||
assert!(info.is_aligned());
|
||||
(
|
||||
&(*info).PathBuffer as *const _ as *const u16,
|
||||
ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
|
||||
(*info).SubstituteNameOffset / 2,
|
||||
(*info).SubstituteNameLength / 2,
|
||||
(*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
|
||||
@ -497,9 +505,10 @@ impl File {
|
||||
}
|
||||
c::IO_REPARSE_TAG_MOUNT_POINT => {
|
||||
let info: *const c::MOUNT_POINT_REPARSE_BUFFER =
|
||||
&buf.rest as *const _ as *const _;
|
||||
ptr::addr_of!((*buf).rest).cast();
|
||||
assert!(info.is_aligned());
|
||||
(
|
||||
&(*info).PathBuffer as *const _ as *const u16,
|
||||
ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
|
||||
(*info).SubstituteNameOffset / 2,
|
||||
(*info).SubstituteNameLength / 2,
|
||||
false,
|
||||
@ -649,18 +658,18 @@ impl File {
|
||||
|
||||
/// A buffer for holding directory entries.
|
||||
struct DirBuff {
|
||||
buffer: Vec<u8>,
|
||||
buffer: Box<Align8<[u8; Self::BUFFER_SIZE]>>,
|
||||
}
|
||||
impl DirBuff {
|
||||
const BUFFER_SIZE: usize = 1024;
|
||||
fn new() -> Self {
|
||||
const BUFFER_SIZE: usize = 1024;
|
||||
Self { buffer: vec![0_u8; BUFFER_SIZE] }
|
||||
Self { buffer: Box::new(Align8([0u8; Self::BUFFER_SIZE])) }
|
||||
}
|
||||
fn capacity(&self) -> usize {
|
||||
self.buffer.len()
|
||||
self.buffer.0.len()
|
||||
}
|
||||
fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||
self.buffer.as_mut_ptr().cast()
|
||||
self.buffer.0.as_mut_ptr().cast()
|
||||
}
|
||||
/// Returns a `DirBuffIter`.
|
||||
fn iter(&self) -> DirBuffIter<'_> {
|
||||
@ -669,7 +678,7 @@ impl DirBuff {
|
||||
}
|
||||
impl AsRef<[u8]> for DirBuff {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.buffer
|
||||
&self.buffer.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -697,9 +706,12 @@ impl<'a> Iterator for DirBuffIter<'a> {
|
||||
// used to get the file name slice.
|
||||
let (name, is_directory, next_entry) = unsafe {
|
||||
let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
|
||||
// Guaranteed to be aligned in documentation for
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
|
||||
assert!(info.is_aligned());
|
||||
let next_entry = (*info).NextEntryOffset as usize;
|
||||
let name = crate::slice::from_raw_parts(
|
||||
(*info).FileName.as_ptr().cast::<u16>(),
|
||||
ptr::addr_of!((*info).FileName).cast::<u16>(),
|
||||
(*info).FileNameLength as usize / size_of::<u16>(),
|
||||
);
|
||||
let is_directory = ((*info).FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
@ -1337,9 +1349,10 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
|
||||
let h = f.as_inner().as_raw_handle();
|
||||
|
||||
unsafe {
|
||||
let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
||||
let db = data.as_mut_ptr() as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
|
||||
let buf = &mut (*db).ReparseTarget as *mut c::WCHAR;
|
||||
let mut data = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
|
||||
let data_ptr = data.0.as_mut_ptr();
|
||||
let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
|
||||
let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
|
||||
let mut i = 0;
|
||||
// FIXME: this conversion is very hacky
|
||||
let v = br"\??\";
|
||||
@ -1359,7 +1372,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
|
||||
cvt(c::DeviceIoControl(
|
||||
h as *mut _,
|
||||
c::FSCTL_SET_REPARSE_POINT,
|
||||
data.as_ptr() as *mut _,
|
||||
data_ptr.cast(),
|
||||
(*db).ReparseDataLength + 8,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
|
@ -329,3 +329,11 @@ pub fn abort_internal() -> ! {
|
||||
}
|
||||
crate::intrinsics::abort();
|
||||
}
|
||||
|
||||
/// Align the inner value to 8 bytes.
|
||||
///
|
||||
/// This is enough for almost all of the buffers we're likely to work with in
|
||||
/// the Windows APIs we use.
|
||||
#[repr(C, align(8))]
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct Align8<T: ?Sized>(pub T);
|
||||
|
@ -432,12 +432,13 @@ impl Step for Llvm {
|
||||
cfg.define("LLVM_LINK_LLVM_DYLIB", "ON");
|
||||
}
|
||||
|
||||
if target.starts_with("riscv") && !target.contains("freebsd") {
|
||||
if target.starts_with("riscv") && !target.contains("freebsd") && !target.contains("openbsd")
|
||||
{
|
||||
// RISC-V GCC erroneously requires linking against
|
||||
// `libatomic` when using 1-byte and 2-byte C++
|
||||
// atomics but the LLVM build system check cannot
|
||||
// detect this. Therefore it is set manually here.
|
||||
// FreeBSD uses Clang as its system compiler and
|
||||
// Some BSD uses Clang as its system compiler and
|
||||
// provides no libatomic in its base system so does
|
||||
// not want this.
|
||||
ldflags.exe.push(" -latomic");
|
||||
|
@ -277,6 +277,7 @@ target | std | host | notes
|
||||
`powerpc64-unknown-linux-musl` | ? | |
|
||||
`powerpc64-wrs-vxworks` | ? | |
|
||||
`powerpc64le-unknown-linux-musl` | ? | |
|
||||
[`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64
|
||||
`riscv32gc-unknown-linux-gnu` | | | RISC-V Linux (kernel 5.4, glibc 2.33)
|
||||
`riscv32gc-unknown-linux-musl` | | | RISC-V Linux (kernel 5.4, musl + RISCV32 support patches)
|
||||
`riscv32im-unknown-none-elf` | * | | Bare RISC-V (RV32IM ISA)
|
||||
@ -284,6 +285,7 @@ target | std | host | notes
|
||||
`riscv32imc-esp-espidf` | ✓ | | RISC-V ESP-IDF
|
||||
`riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD
|
||||
`riscv64gc-unknown-linux-musl` | | | RISC-V Linux (kernel 4.20, musl 1.2.0)
|
||||
[`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64
|
||||
`s390x-unknown-linux-musl` | | | S390x Linux (kernel 3.2, MUSL)
|
||||
`sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux
|
||||
`sparc64-unknown-netbsd` | ✓ | ✓ | NetBSD/sparc64
|
||||
|
@ -12,6 +12,8 @@ The target names follow this format: `$ARCH-unknown-openbsd`, where `$ARCH` spec
|
||||
|--------------------------------|-------------|------------------|
|
||||
| `aarch64-unknown-openbsd` | libc++ | [64-bit ARM systems](https://www.openbsd.org/arm64.html) |
|
||||
| `i686-unknown-openbsd` | libc++ | [Standard PC and clones based on the Intel i386 architecture and compatible processors](https://www.openbsd.org/i386.html) |
|
||||
| `powerpc64-unknown-openbsd` | libc++ | [IBM POWER-based PowerNV systems](https://www.openbsd.org/powerpc64.html) |
|
||||
| `riscv64gc-unknown-openbsd` | libc++ | [64-bit RISC-V systems](https://www.openbsd.org/riscv64.html) |
|
||||
| `sparc64-unknown-openbsd` | estdc++ | [Sun UltraSPARC and Fujitsu SPARC64 systems](https://www.openbsd.org/sparc64.html) |
|
||||
| `x86_64-unknown-openbsd` | libc++ | [AMD64-based systems](https://www.openbsd.org/amd64.html) |
|
||||
|
||||
|
@ -167,8 +167,8 @@ enum P {
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
enum Q {
|
||||
#[bar]
|
||||
//~^ ERROR `#[bar]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
//~^ ERROR `#[bar]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
A {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@ -179,8 +179,8 @@ enum Q {
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
enum R {
|
||||
#[bar = "..."]
|
||||
//~^ ERROR `#[bar = ...]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
//~^ ERROR `#[bar = ...]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
A {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@ -191,8 +191,8 @@ enum R {
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
enum S {
|
||||
#[bar = 4]
|
||||
//~^ ERROR `#[bar = ...]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
//~^ ERROR `#[bar = ...]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
A {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@ -203,8 +203,8 @@ enum S {
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
enum T {
|
||||
#[bar("...")]
|
||||
//~^ ERROR `#[bar("...")]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
//~^ ERROR `#[bar(...)]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
A {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@ -215,7 +215,7 @@ enum T {
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
enum U {
|
||||
#[label(code = "...")]
|
||||
//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
|
||||
//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
|
||||
A {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@ -232,7 +232,7 @@ enum V {
|
||||
var: String,
|
||||
},
|
||||
B {
|
||||
//~^ ERROR subdiagnostic kind not specified
|
||||
//~^ ERROR subdiagnostic kind not specified
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
var: String,
|
||||
@ -310,10 +310,8 @@ union AC {
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[label(parser::add_paren)]
|
||||
//~^ NOTE previously specified here
|
||||
//~^^ NOTE previously specified here
|
||||
#[label(parser::add_paren)]
|
||||
//~^ ERROR specified multiple times
|
||||
//~^^ ERROR specified multiple times
|
||||
struct AD {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@ -331,16 +329,16 @@ struct AE {
|
||||
#[label(parser::add_paren)]
|
||||
struct AF {
|
||||
#[primary_span]
|
||||
//~^ NOTE previously specified here
|
||||
//~^ NOTE previously specified here
|
||||
span_a: Span,
|
||||
#[primary_span]
|
||||
//~^ ERROR specified multiple times
|
||||
//~^ ERROR specified multiple times
|
||||
span_b: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
struct AG {
|
||||
//~^ ERROR subdiagnostic kind not specified
|
||||
//~^ ERROR subdiagnostic kind not specified
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
}
|
||||
@ -392,27 +390,25 @@ struct AK {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
#[applicability]
|
||||
//~^ NOTE previously specified here
|
||||
//~^ NOTE previously specified here
|
||||
applicability_a: Applicability,
|
||||
#[applicability]
|
||||
//~^ ERROR specified multiple times
|
||||
//~^ ERROR specified multiple times
|
||||
applicability_b: Applicability,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[suggestion(parser::add_paren, code = "...")]
|
||||
//~^ ERROR suggestion without `applicability`
|
||||
struct AL {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
#[applicability]
|
||||
//~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability`
|
||||
//~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability`
|
||||
applicability: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[suggestion(parser::add_paren, code = "...")]
|
||||
//~^ ERROR suggestion without `applicability`
|
||||
struct AM {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@ -448,8 +444,7 @@ struct AQ;
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[suggestion(parser::add_paren, code = "...")]
|
||||
//~^ ERROR suggestion without `applicability`
|
||||
//~^^ ERROR suggestion without `#[primary_span]` field
|
||||
//~^ ERROR suggestion without `#[primary_span]` field
|
||||
struct AR {
|
||||
var: String,
|
||||
}
|
||||
@ -519,3 +514,120 @@ struct AZ {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[suggestion(parser::add_paren, code = "...")]
|
||||
//~^ ERROR suggestion without `#[primary_span]` field
|
||||
struct BA {
|
||||
#[suggestion_part]
|
||||
//~^ ERROR `#[suggestion_part]` is not a valid attribute
|
||||
span: Span,
|
||||
#[suggestion_part(code = "...")]
|
||||
//~^ ERROR `#[suggestion_part(...)]` is not a valid attribute
|
||||
span2: Span,
|
||||
#[applicability]
|
||||
applicability: Applicability,
|
||||
var: String,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
|
||||
//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields
|
||||
//~| ERROR `code` is not a valid nested attribute of a `multipart_suggestion` attribute
|
||||
struct BBa {
|
||||
var: String,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
struct BBb {
|
||||
#[suggestion_part]
|
||||
//~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
span1: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
struct BBc {
|
||||
#[suggestion_part()]
|
||||
//~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
span1: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren)]
|
||||
//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields
|
||||
struct BC {
|
||||
#[primary_span]
|
||||
//~^ ERROR `#[primary_span]` is not a valid attribute
|
||||
span: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren)]
|
||||
struct BD {
|
||||
#[suggestion_part]
|
||||
//~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
span1: Span,
|
||||
#[suggestion_part()]
|
||||
//~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
span2: Span,
|
||||
#[suggestion_part(foo = "bar")]
|
||||
//~^ ERROR `#[suggestion_part(foo = ...)]` is not a valid attribute
|
||||
span4: Span,
|
||||
#[suggestion_part(code = "...")]
|
||||
//~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
||||
s1: String,
|
||||
#[suggestion_part()]
|
||||
//~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
||||
s2: String,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
struct BE {
|
||||
#[suggestion_part(code = "...", code = ",,,")]
|
||||
//~^ ERROR specified multiple times
|
||||
//~| NOTE previously specified here
|
||||
span: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
struct BF {
|
||||
#[suggestion_part(code = "(")]
|
||||
first: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
second: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren)]
|
||||
struct BG {
|
||||
#[applicability]
|
||||
appl: Applicability,
|
||||
#[suggestion_part(code = "(")]
|
||||
first: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
second: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
//~^ NOTE previously specified here
|
||||
struct BH {
|
||||
#[applicability]
|
||||
//~^ ERROR specified multiple times
|
||||
appl: Applicability,
|
||||
#[suggestion_part(code = "(")]
|
||||
first: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
second: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
struct BI {
|
||||
#[suggestion_part(code = "")]
|
||||
spans: Vec<Span>,
|
||||
}
|
||||
|
@ -65,16 +65,16 @@ LL | #[label()]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `code` is not a valid nested attribute of a `label` attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:137:1
|
||||
--> $DIR/subdiagnostic-derive.rs:137:28
|
||||
|
|
||||
LL | #[label(parser::add_paren, code = "...")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: `applicability` is not a valid nested attribute of a `label` attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:146:1
|
||||
--> $DIR/subdiagnostic-derive.rs:146:28
|
||||
|
|
||||
LL | #[label(parser::add_paren, applicability = "machine-applicable")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unsupported type attribute for subdiagnostic enum
|
||||
--> $DIR/subdiagnostic-derive.rs:155:1
|
||||
@ -100,13 +100,11 @@ error: `#[bar = ...]` is not a valid attribute
|
||||
LL | #[bar = 4]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `#[bar("...")]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:205:11
|
||||
error: `#[bar(...)]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:205:5
|
||||
|
|
||||
LL | #[bar("...")]
|
||||
| ^^^^^
|
||||
|
|
||||
= help: first argument of the attribute should be the diagnostic slug
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: diagnostic slug must be first argument of a `#[label(...)]` attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:217:5
|
||||
@ -163,6 +161,8 @@ error: `#[bar(...)]` is not a valid attribute
|
||||
|
|
||||
LL | #[bar("...")]
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes
|
||||
|
||||
error: unexpected unsupported untagged union
|
||||
--> $DIR/subdiagnostic-derive.rs:304:1
|
||||
@ -175,19 +175,7 @@ LL | | }
|
||||
| |_^
|
||||
|
||||
error: specified multiple times
|
||||
--> $DIR/subdiagnostic-derive.rs:314:1
|
||||
|
|
||||
LL | #[label(parser::add_paren)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: previously specified here
|
||||
--> $DIR/subdiagnostic-derive.rs:311:1
|
||||
|
|
||||
LL | #[label(parser::add_paren)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: specified multiple times
|
||||
--> $DIR/subdiagnostic-derive.rs:314:1
|
||||
--> $DIR/subdiagnostic-derive.rs:313:1
|
||||
|
|
||||
LL | #[label(parser::add_paren)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -199,7 +187,7 @@ LL | #[label(parser::add_paren)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `#[label(parser::add_paren)]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:323:28
|
||||
--> $DIR/subdiagnostic-derive.rs:321:28
|
||||
|
|
||||
LL | #[label(parser::add_paren, parser::add_paren)]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
@ -207,134 +195,226 @@ LL | #[label(parser::add_paren, parser::add_paren)]
|
||||
= help: a diagnostic slug must be the first argument to the attribute
|
||||
|
||||
error: specified multiple times
|
||||
--> $DIR/subdiagnostic-derive.rs:336:5
|
||||
--> $DIR/subdiagnostic-derive.rs:334:5
|
||||
|
|
||||
LL | #[primary_span]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: previously specified here
|
||||
--> $DIR/subdiagnostic-derive.rs:333:5
|
||||
--> $DIR/subdiagnostic-derive.rs:331:5
|
||||
|
|
||||
LL | #[primary_span]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: subdiagnostic kind not specified
|
||||
--> $DIR/subdiagnostic-derive.rs:342:8
|
||||
--> $DIR/subdiagnostic-derive.rs:340:8
|
||||
|
|
||||
LL | struct AG {
|
||||
| ^^
|
||||
|
||||
error: specified multiple times
|
||||
--> $DIR/subdiagnostic-derive.rs:379:47
|
||||
--> $DIR/subdiagnostic-derive.rs:377:47
|
||||
|
|
||||
LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
note: previously specified here
|
||||
--> $DIR/subdiagnostic-derive.rs:379:33
|
||||
--> $DIR/subdiagnostic-derive.rs:377:33
|
||||
|
|
||||
LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: specified multiple times
|
||||
--> $DIR/subdiagnostic-derive.rs:397:5
|
||||
--> $DIR/subdiagnostic-derive.rs:395:5
|
||||
|
|
||||
LL | #[applicability]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: previously specified here
|
||||
--> $DIR/subdiagnostic-derive.rs:394:5
|
||||
--> $DIR/subdiagnostic-derive.rs:392:5
|
||||
|
|
||||
LL | #[applicability]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: the `#[applicability]` attribute can only be applied to fields of type `Applicability`
|
||||
--> $DIR/subdiagnostic-derive.rs:408:5
|
||||
--> $DIR/subdiagnostic-derive.rs:405:5
|
||||
|
|
||||
LL | #[applicability]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: suggestion without `applicability`
|
||||
--> $DIR/subdiagnostic-derive.rs:403:1
|
||||
|
|
||||
LL | / #[suggestion(parser::add_paren, code = "...")]
|
||||
LL | |
|
||||
LL | | struct AL {
|
||||
LL | | #[primary_span]
|
||||
... |
|
||||
LL | | applicability: Span,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: suggestion without `applicability`
|
||||
--> $DIR/subdiagnostic-derive.rs:414:1
|
||||
|
|
||||
LL | / #[suggestion(parser::add_paren, code = "...")]
|
||||
LL | |
|
||||
LL | | struct AM {
|
||||
LL | | #[primary_span]
|
||||
LL | | span: Span,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: suggestion without `code = "..."`
|
||||
--> $DIR/subdiagnostic-derive.rs:422:1
|
||||
--> $DIR/subdiagnostic-derive.rs:418:1
|
||||
|
|
||||
LL | / #[suggestion(parser::add_paren)]
|
||||
LL | |
|
||||
LL | | struct AN {
|
||||
LL | | #[primary_span]
|
||||
... |
|
||||
LL | | applicability: Applicability,
|
||||
LL | | }
|
||||
| |_^
|
||||
LL | #[suggestion(parser::add_paren)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: invalid applicability
|
||||
--> $DIR/subdiagnostic-derive.rs:432:46
|
||||
--> $DIR/subdiagnostic-derive.rs:428:46
|
||||
|
|
||||
LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: suggestion without `applicability`
|
||||
--> $DIR/subdiagnostic-derive.rs:450:1
|
||||
|
|
||||
LL | / #[suggestion(parser::add_paren, code = "...")]
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | struct AR {
|
||||
LL | | var: String,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: suggestion without `#[primary_span]` field
|
||||
--> $DIR/subdiagnostic-derive.rs:450:1
|
||||
--> $DIR/subdiagnostic-derive.rs:446:1
|
||||
|
|
||||
LL | / #[suggestion(parser::add_paren, code = "...")]
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | struct AR {
|
||||
LL | | var: String,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: unsupported type attribute for subdiagnostic enum
|
||||
--> $DIR/subdiagnostic-derive.rs:465:1
|
||||
--> $DIR/subdiagnostic-derive.rs:460:1
|
||||
|
|
||||
LL | #[label]
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `var` doesn't refer to a field on this type
|
||||
--> $DIR/subdiagnostic-derive.rs:485:39
|
||||
--> $DIR/subdiagnostic-derive.rs:480:39
|
||||
|
|
||||
LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
|
||||
| ^^^^^^^
|
||||
|
||||
error: `var` doesn't refer to a field on this type
|
||||
--> $DIR/subdiagnostic-derive.rs:504:43
|
||||
--> $DIR/subdiagnostic-derive.rs:499:43
|
||||
|
|
||||
LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
|
||||
| ^^^^^^^
|
||||
|
||||
error: `#[suggestion_part]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:522:5
|
||||
|
|
||||
LL | #[suggestion_part]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead
|
||||
|
||||
error: `#[suggestion_part(...)]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:525:5
|
||||
|
|
||||
LL | #[suggestion_part(code = "...")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: `#[suggestion_part(...)]` is only valid in multipart suggestions
|
||||
|
||||
error: suggestion without `#[primary_span]` field
|
||||
--> $DIR/subdiagnostic-derive.rs:519:1
|
||||
|
|
||||
LL | / #[suggestion(parser::add_paren, code = "...")]
|
||||
LL | |
|
||||
LL | | struct BA {
|
||||
LL | | #[suggestion_part]
|
||||
... |
|
||||
LL | | var: String,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: `code` is not a valid nested attribute of a `multipart_suggestion` attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:534:43
|
||||
|
|
||||
LL | #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: multipart suggestion without any `#[suggestion_part(...)]` fields
|
||||
--> $DIR/subdiagnostic-derive.rs:534:1
|
||||
|
|
||||
LL | / #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | struct BBa {
|
||||
LL | | var: String,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
--> $DIR/subdiagnostic-derive.rs:544:5
|
||||
|
|
||||
LL | #[suggestion_part]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
--> $DIR/subdiagnostic-derive.rs:552:5
|
||||
|
|
||||
LL | #[suggestion_part()]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `#[primary_span]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:561:5
|
||||
|
|
||||
LL | #[primary_span]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]`
|
||||
|
||||
error: multipart suggestion without any `#[suggestion_part(...)]` fields
|
||||
--> $DIR/subdiagnostic-derive.rs:558:1
|
||||
|
|
||||
LL | / #[multipart_suggestion(parser::add_paren)]
|
||||
LL | |
|
||||
LL | | struct BC {
|
||||
LL | | #[primary_span]
|
||||
LL | |
|
||||
LL | | span: Span,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
--> $DIR/subdiagnostic-derive.rs:569:5
|
||||
|
|
||||
LL | #[suggestion_part]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
--> $DIR/subdiagnostic-derive.rs:572:5
|
||||
|
|
||||
LL | #[suggestion_part()]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `#[suggestion_part(foo = ...)]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:575:23
|
||||
|
|
||||
LL | #[suggestion_part(foo = "bar")]
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: `code` is the only valid nested attribute
|
||||
|
||||
error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
||||
--> $DIR/subdiagnostic-derive.rs:578:5
|
||||
|
|
||||
LL | #[suggestion_part(code = "...")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
||||
--> $DIR/subdiagnostic-derive.rs:581:5
|
||||
|
|
||||
LL | #[suggestion_part()]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: specified multiple times
|
||||
--> $DIR/subdiagnostic-derive.rs:589:37
|
||||
|
|
||||
LL | #[suggestion_part(code = "...", code = ",,,")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
note: previously specified here
|
||||
--> $DIR/subdiagnostic-derive.rs:589:23
|
||||
|
|
||||
LL | #[suggestion_part(code = "...", code = ",,,")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: specified multiple times
|
||||
--> $DIR/subdiagnostic-derive.rs:619:5
|
||||
|
|
||||
LL | #[applicability]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: previously specified here
|
||||
--> $DIR/subdiagnostic-derive.rs:616:43
|
||||
|
|
||||
LL | #[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot find attribute `foo` in this scope
|
||||
--> $DIR/subdiagnostic-derive.rs:63:3
|
||||
|
|
||||
@ -395,6 +475,6 @@ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent`
|
||||
LL | #[label(slug)]
|
||||
| ^^^^ not found in `rustc_errors::fluent`
|
||||
|
||||
error: aborting due to 52 previous errors
|
||||
error: aborting due to 64 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0425`.
|
||||
|
@ -11,7 +11,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>;
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::bar::<'static, char>}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>();
|
||||
| ++
|
||||
@ -29,7 +29,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>;
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found fn item `fn() {<i8 as Foo<'static, 'static>>::bar::<'static, char>}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>();
|
||||
| ++
|
||||
@ -47,7 +47,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz;
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::baz}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz();
|
||||
| ++
|
||||
|
@ -11,7 +11,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>;
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::bar::<ReStatic, char>}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>();
|
||||
| ++
|
||||
@ -29,7 +29,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>;
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>();
|
||||
| ++
|
||||
@ -47,7 +47,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz;
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::baz}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz();
|
||||
| ++
|
||||
|
@ -5,6 +5,11 @@ LL | if foo == y {}
|
||||
| --- ^^ - _
|
||||
| |
|
||||
| for<'r> fn(&'r i32) -> &'r i32 {foo}
|
||||
|
|
||||
help: use parentheses to call this function
|
||||
|
|
||||
LL | if foo(/* &i32 */) == y {}
|
||||
| ++++++++++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -13,10 +13,10 @@ LL | trait NonObjectSafe1: Sized {}
|
||||
| this trait cannot be made into an object...
|
||||
|
||||
error[E0038]: the trait `NonObjectSafe2` cannot be made into an object
|
||||
--> $DIR/feature-gate-object_safe_for_dispatch.rs:22:36
|
||||
--> $DIR/feature-gate-object_safe_for_dispatch.rs:22:45
|
||||
|
|
||||
LL | fn return_non_object_safe_ref() -> &'static dyn NonObjectSafe2 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NonObjectSafe2` cannot be made into an object
|
||||
| ^^^^^^^^^^^^^^^^^^ `NonObjectSafe2` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/feature-gate-object_safe_for_dispatch.rs:7:8
|
||||
@ -50,10 +50,10 @@ LL | fn foo<T>(&self);
|
||||
= help: consider moving `foo` to another trait
|
||||
|
||||
error[E0038]: the trait `NonObjectSafe4` cannot be made into an object
|
||||
--> $DIR/feature-gate-object_safe_for_dispatch.rs:31:35
|
||||
--> $DIR/feature-gate-object_safe_for_dispatch.rs:31:47
|
||||
|
|
||||
LL | fn return_non_object_safe_rc() -> std::rc::Rc<dyn NonObjectSafe4> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NonObjectSafe4` cannot be made into an object
|
||||
| ^^^^^^^^^^^^^^^^^^ `NonObjectSafe4` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/feature-gate-object_safe_for_dispatch.rs:15:22
|
||||
|
@ -6,14 +6,10 @@ LL | let x = f == g;
|
||||
| |
|
||||
| fn() {f}
|
||||
|
|
||||
help: you might have forgotten to call this function
|
||||
help: use parentheses to call these
|
||||
|
|
||||
LL | let x = f() == g;
|
||||
| ++
|
||||
help: you might have forgotten to call this function
|
||||
|
|
||||
LL | let x = f == g();
|
||||
| ++
|
||||
LL | let x = f() == g();
|
||||
| ++ ++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-compare-mismatch.rs:4:18
|
||||
|
@ -8,6 +8,10 @@ LL | let _: () = Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>;
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found struct `Box<dyn FnOnce(isize)>`
|
||||
help: use parentheses to call this trait object
|
||||
|
|
||||
LL | let _: () = (Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>)(/* isize */);
|
||||
| + ++++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-trait-formatting.rs:10:17
|
||||
@ -19,6 +23,10 @@ LL | let _: () = Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found struct `Box<dyn Fn(isize, isize)>`
|
||||
help: use parentheses to call this trait object
|
||||
|
|
||||
LL | let _: () = (Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>)(/* isize */, /* isize */);
|
||||
| + +++++++++++++++++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-trait-formatting.rs:14:17
|
||||
|
@ -1,16 +1,28 @@
|
||||
// This test case checks the behavior of typeck::check::method::suggest::is_fn on Ty::Error.
|
||||
|
||||
struct Foo;
|
||||
|
||||
trait Bar {
|
||||
//~^ NOTE `Bar` defines an item `bar`, perhaps you need to implement it
|
||||
//~| NOTE `Bar` defines an item `bar`, perhaps you need to implement it
|
||||
fn bar(&self) {}
|
||||
}
|
||||
|
||||
impl Bar for Foo {}
|
||||
|
||||
fn main() {
|
||||
let arc = std::sync::Arc::new(oops);
|
||||
//~^ ERROR cannot find value `oops` in this scope
|
||||
//~| NOTE not found
|
||||
// The error "note: this is a function, perhaps you wish to call it" MUST NOT appear.
|
||||
arc.blablabla();
|
||||
//~^ ERROR no method named `blablabla`
|
||||
arc.bar();
|
||||
//~^ ERROR no method named `bar`
|
||||
//~| NOTE method not found
|
||||
let arc2 = std::sync::Arc::new(|| 1);
|
||||
// The error "note: this is a function, perhaps you wish to call it" SHOULD appear
|
||||
arc2.blablabla();
|
||||
//~^ ERROR no method named `blablabla`
|
||||
//~| HELP items from traits can only be used if the trait is implemented and in scope
|
||||
|
||||
let arc2 = std::sync::Arc::new(|| Foo);
|
||||
arc2.bar();
|
||||
//~^ ERROR no method named `bar`
|
||||
//~| NOTE method not found
|
||||
//~| NOTE this is a function, perhaps you wish to call it
|
||||
//~| HELP items from traits can only be used if the trait is implemented and in scope
|
||||
//~| HELP use parentheses to call this closure
|
||||
}
|
||||
|
@ -1,22 +1,38 @@
|
||||
error[E0425]: cannot find value `oops` in this scope
|
||||
--> $DIR/fn-help-with-err.rs:3:35
|
||||
--> $DIR/fn-help-with-err.rs:14:35
|
||||
|
|
||||
LL | let arc = std::sync::Arc::new(oops);
|
||||
| ^^^^ not found in this scope
|
||||
|
||||
error[E0599]: no method named `blablabla` found for struct `Arc<_>` in the current scope
|
||||
--> $DIR/fn-help-with-err.rs:7:9
|
||||
error[E0599]: no method named `bar` found for struct `Arc<_>` in the current scope
|
||||
--> $DIR/fn-help-with-err.rs:17:9
|
||||
|
|
||||
LL | arc.blablabla();
|
||||
| ^^^^^^^^^ method not found in `Arc<_>`
|
||||
LL | arc.bar();
|
||||
| ^^^ method not found in `Arc<_>`
|
||||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
note: `Bar` defines an item `bar`, perhaps you need to implement it
|
||||
--> $DIR/fn-help-with-err.rs:5:1
|
||||
|
|
||||
LL | trait Bar {
|
||||
| ^^^^^^^^^
|
||||
|
||||
error[E0599]: no method named `blablabla` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>` in the current scope
|
||||
--> $DIR/fn-help-with-err.rs:12:10
|
||||
error[E0599]: no method named `bar` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>` in the current scope
|
||||
--> $DIR/fn-help-with-err.rs:23:10
|
||||
|
|
||||
LL | arc2.blablabla();
|
||||
| ---- ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>`
|
||||
| |
|
||||
| this is a function, perhaps you wish to call it
|
||||
LL | arc2.bar();
|
||||
| ^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>`
|
||||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
note: `Bar` defines an item `bar`, perhaps you need to implement it
|
||||
--> $DIR/fn-help-with-err.rs:5:1
|
||||
|
|
||||
LL | trait Bar {
|
||||
| ^^^^^^^^^
|
||||
help: use parentheses to call this closure
|
||||
|
|
||||
LL | arc2().bar();
|
||||
| ++
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
@ -21,10 +21,10 @@ LL | fn foo() -> Self where Self: Sized;
|
||||
| +++++++++++++++++
|
||||
|
||||
error[E0038]: the trait `NotObjectSafe` cannot be made into an object
|
||||
--> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:28:13
|
||||
--> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:28:17
|
||||
|
|
||||
LL | fn cat() -> Box<dyn NotObjectSafe> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ `NotObjectSafe` cannot be made into an object
|
||||
| ^^^^^^^^^^^^^^^^^ `NotObjectSafe` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:3:8
|
||||
|
@ -11,7 +11,7 @@ LL | fn opaque() -> impl Fn() -> i32 {
|
||||
|
|
||||
= note: expected type `i32`
|
||||
found opaque type `impl Fn() -> i32`
|
||||
help: use parentheses to call this closure
|
||||
help: use parentheses to call this opaque type
|
||||
|
|
||||
LL | opaque()()
|
||||
| ++
|
||||
|
@ -13,8 +13,8 @@ LL | fn test() -> Foo { Foo }
|
||||
found fn item `fn(u32) -> Foo {Foo}`
|
||||
help: use parentheses to instantiate this tuple struct
|
||||
|
|
||||
LL | fn test() -> Foo { Foo(_) }
|
||||
| +++
|
||||
LL | fn test() -> Foo { Foo(/* u32 */) }
|
||||
| +++++++++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -2,9 +2,7 @@ error[E0599]: no method named `f` found for fn pointer `fn(&u8)` in the current
|
||||
--> $DIR/issue-57362-1.rs:20:7
|
||||
|
|
||||
LL | a.f();
|
||||
| - ^ method not found in `fn(&u8)`
|
||||
| |
|
||||
| this is a function, perhaps you wish to call it
|
||||
| ^ method not found in `fn(&u8)`
|
||||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
note: `Trait` defines an item `f`, perhaps you need to implement it
|
||||
|
@ -6,7 +6,7 @@ LL | foo > 12;
|
||||
| |
|
||||
| fn() -> i32 {foo}
|
||||
|
|
||||
help: you might have forgotten to call this function
|
||||
help: use parentheses to call this function
|
||||
|
|
||||
LL | foo() > 12;
|
||||
| ++
|
||||
@ -28,10 +28,10 @@ LL | bar > 13;
|
||||
| |
|
||||
| fn(i64) -> i64 {bar}
|
||||
|
|
||||
help: you might have forgotten to call this function
|
||||
help: use parentheses to call this function
|
||||
|
|
||||
LL | bar( /* arguments */ ) > 13;
|
||||
| +++++++++++++++++++
|
||||
LL | bar(/* i64 */) > 13;
|
||||
| +++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-59488.rs:18:11
|
||||
@ -50,14 +50,10 @@ LL | foo > foo;
|
||||
| |
|
||||
| fn() -> i32 {foo}
|
||||
|
|
||||
help: you might have forgotten to call this function
|
||||
help: use parentheses to call these
|
||||
|
|
||||
LL | foo() > foo;
|
||||
| ++
|
||||
help: you might have forgotten to call this function
|
||||
|
|
||||
LL | foo > foo();
|
||||
| ++
|
||||
LL | foo() > foo();
|
||||
| ++ ++
|
||||
|
||||
error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}`
|
||||
--> $DIR/issue-59488.rs:25:9
|
||||
|
@ -8,11 +8,6 @@ LL | assert_eq!(a, 0);
|
||||
| {integer}
|
||||
|
|
||||
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: you might have forgotten to call this function
|
||||
--> $SRC_DIR/core/src/macros/mod.rs:LL:COL
|
||||
|
|
||||
LL | if !(*left_val() == *right_val) {
|
||||
| ++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-70724-add_type_neq_err_label-unwrap.rs:6:5
|
||||
@ -21,7 +16,7 @@ LL | assert_eq!(a, 0);
|
||||
| ^^^^^^^^^^^^^^^^ expected fn item, found integer
|
||||
|
|
||||
= note: expected fn item `fn() -> i32 {a}`
|
||||
found type `i32`
|
||||
found type `{integer}`
|
||||
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: `fn() -> i32 {a}` doesn't implement `Debug`
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0038]: the trait `Bar` cannot be made into an object
|
||||
--> $DIR/object-safety-associated-consts.rs:12:30
|
||||
--> $DIR/object-safety-associated-consts.rs:12:31
|
||||
|
|
||||
LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
|
||||
| ^^^^^^^^ `Bar` cannot be made into an object
|
||||
| ^^^^^^^ `Bar` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/object-safety-associated-consts.rs:9:11
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0038]: the trait `X` cannot be made into an object
|
||||
--> $DIR/object-safety-bounds.rs:7:11
|
||||
--> $DIR/object-safety-bounds.rs:7:15
|
||||
|
|
||||
LL | fn f() -> Box<dyn X<U = u32>> {
|
||||
| ^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object
|
||||
| ^^^^^^^^^^^^^^ `X` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/object-safety-bounds.rs:4:13
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0038]: the trait `Bar` cannot be made into an object
|
||||
--> $DIR/object-safety-generics.rs:18:30
|
||||
--> $DIR/object-safety-generics.rs:18:31
|
||||
|
|
||||
LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
|
||||
| ^^^^^^^^ `Bar` cannot be made into an object
|
||||
| ^^^^^^^ `Bar` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/object-safety-generics.rs:10:8
|
||||
@ -14,10 +14,10 @@ LL | fn bar<T>(&self, t: T);
|
||||
= help: consider moving `bar` to another trait
|
||||
|
||||
error[E0038]: the trait `Bar` cannot be made into an object
|
||||
--> $DIR/object-safety-generics.rs:24:39
|
||||
--> $DIR/object-safety-generics.rs:24:40
|
||||
|
|
||||
LL | fn make_bar_explicit<T:Bar>(t: &T) -> &dyn Bar {
|
||||
| ^^^^^^^^ `Bar` cannot be made into an object
|
||||
| ^^^^^^^ `Bar` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/object-safety-generics.rs:10:8
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0038]: the trait `Bar` cannot be made into an object
|
||||
--> $DIR/object-safety-mentions-Self.rs:22:30
|
||||
--> $DIR/object-safety-mentions-Self.rs:22:31
|
||||
|
|
||||
LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
|
||||
| ^^^^^^^^ `Bar` cannot be made into an object
|
||||
| ^^^^^^^ `Bar` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/object-safety-mentions-Self.rs:11:22
|
||||
@ -14,10 +14,10 @@ LL | fn bar(&self, x: &Self);
|
||||
= help: consider moving `bar` to another trait
|
||||
|
||||
error[E0038]: the trait `Baz` cannot be made into an object
|
||||
--> $DIR/object-safety-mentions-Self.rs:28:30
|
||||
--> $DIR/object-safety-mentions-Self.rs:28:31
|
||||
|
|
||||
LL | fn make_baz<T:Baz>(t: &T) -> &dyn Baz {
|
||||
| ^^^^^^^^ `Baz` cannot be made into an object
|
||||
| ^^^^^^^ `Baz` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/object-safety-mentions-Self.rs:15:22
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0038]: the trait `Foo` cannot be made into an object
|
||||
--> $DIR/object-safety-no-static.rs:12:18
|
||||
--> $DIR/object-safety-no-static.rs:12:22
|
||||
|
|
||||
LL | fn diverges() -> Box<dyn Foo> {
|
||||
| ^^^^^^^^^^^^ `Foo` cannot be made into an object
|
||||
| ^^^^^^^ `Foo` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/object-safety-no-static.rs:9:8
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0038]: the trait `Bar` cannot be made into an object
|
||||
--> $DIR/object-safety-sized-2.rs:14:30
|
||||
--> $DIR/object-safety-sized-2.rs:14:31
|
||||
|
|
||||
LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
|
||||
| ^^^^^^^^ `Bar` cannot be made into an object
|
||||
| ^^^^^^^ `Bar` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/object-safety-sized-2.rs:9:18
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0038]: the trait `Bar` cannot be made into an object
|
||||
--> $DIR/object-safety-sized.rs:12:30
|
||||
--> $DIR/object-safety-sized.rs:12:31
|
||||
|
|
||||
LL | fn make_bar<T:Bar>(t: &T) -> &dyn Bar {
|
||||
| ^^^^^^^^ `Bar` cannot be made into an object
|
||||
| ^^^^^^^ `Bar` cannot be made into an object
|
||||
|
|
||||
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
||||
--> $DIR/object-safety-sized.rs:8:13
|
||||
|
@ -329,8 +329,8 @@ LL | let _: Z = Z::Fn;
|
||||
found fn item `fn(u8) -> Z {Z::Fn}`
|
||||
help: use parentheses to instantiate this tuple variant
|
||||
|
|
||||
LL | let _: Z = Z::Fn(_);
|
||||
| +++
|
||||
LL | let _: Z = Z::Fn(/* u8 */);
|
||||
| ++++++++++
|
||||
|
||||
error[E0618]: expected function, found enum variant `Z::Unit`
|
||||
--> $DIR/privacy-enum-ctor.rs:31:17
|
||||
@ -364,8 +364,8 @@ LL | let _: E = m::E::Fn;
|
||||
found fn item `fn(u8) -> E {E::Fn}`
|
||||
help: use parentheses to instantiate this tuple variant
|
||||
|
|
||||
LL | let _: E = m::E::Fn(_);
|
||||
| +++
|
||||
LL | let _: E = m::E::Fn(/* u8 */);
|
||||
| ++++++++++
|
||||
|
||||
error[E0618]: expected function, found enum variant `m::E::Unit`
|
||||
--> $DIR/privacy-enum-ctor.rs:47:16
|
||||
@ -399,8 +399,8 @@ LL | let _: E = E::Fn;
|
||||
found fn item `fn(u8) -> E {E::Fn}`
|
||||
help: use parentheses to instantiate this tuple variant
|
||||
|
|
||||
LL | let _: E = E::Fn(_);
|
||||
| +++
|
||||
LL | let _: E = E::Fn(/* u8 */);
|
||||
| ++++++++++
|
||||
|
||||
error[E0618]: expected function, found enum variant `E::Unit`
|
||||
--> $DIR/privacy-enum-ctor.rs:55:16
|
||||
|
7
src/test/ui/suggestions/call-boxed.rs
Normal file
7
src/test/ui/suggestions/call-boxed.rs
Normal file
@ -0,0 +1,7 @@
|
||||
fn main() {
|
||||
let mut x = 1i32;
|
||||
let y = Box::new(|| 1);
|
||||
x = y;
|
||||
//~^ ERROR mismatched types
|
||||
//~| HELP use parentheses to call this closure
|
||||
}
|
20
src/test/ui/suggestions/call-boxed.stderr
Normal file
20
src/test/ui/suggestions/call-boxed.stderr
Normal file
@ -0,0 +1,20 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/call-boxed.rs:4:9
|
||||
|
|
||||
LL | let mut x = 1i32;
|
||||
| ---- expected due to this value
|
||||
LL | let y = Box::new(|| 1);
|
||||
| -- the found closure
|
||||
LL | x = y;
|
||||
| ^ expected `i32`, found struct `Box`
|
||||
|
|
||||
= note: expected type `i32`
|
||||
found struct `Box<[closure@$DIR/call-boxed.rs:3:22: 3:24]>`
|
||||
help: use parentheses to call this closure
|
||||
|
|
||||
LL | x = y();
|
||||
| ++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
39
src/test/ui/suggestions/call-on-missing.rs
Normal file
39
src/test/ui/suggestions/call-on-missing.rs
Normal file
@ -0,0 +1,39 @@
|
||||
struct Foo { i: i32 }
|
||||
|
||||
impl Foo {
|
||||
fn bar(&self) {}
|
||||
}
|
||||
|
||||
fn foo() -> Foo {
|
||||
Foo { i: 1 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo.bar();
|
||||
//~^ ERROR no method named `bar`
|
||||
//~| HELP use parentheses to call this function
|
||||
|
||||
foo.i;
|
||||
//~^ ERROR no field `i`
|
||||
//~| HELP use parentheses to call this function
|
||||
|
||||
let callable = Box::new(|| Foo { i: 1 }) as Box<dyn Fn() -> Foo>;
|
||||
|
||||
callable.bar();
|
||||
//~^ ERROR no method named `bar`
|
||||
//~| HELP use parentheses to call this trait object
|
||||
|
||||
callable.i;
|
||||
//~^ ERROR no field `i`
|
||||
//~| HELP use parentheses to call this trait object
|
||||
}
|
||||
|
||||
fn type_param<T: Fn() -> Foo>(t: T) {
|
||||
t.bar();
|
||||
//~^ ERROR no method named `bar`
|
||||
//~| HELP use parentheses to call this type parameter
|
||||
|
||||
t.i;
|
||||
//~^ ERROR no field `i`
|
||||
//~| HELP use parentheses to call this type parameter
|
||||
}
|
75
src/test/ui/suggestions/call-on-missing.stderr
Normal file
75
src/test/ui/suggestions/call-on-missing.stderr
Normal file
@ -0,0 +1,75 @@
|
||||
error[E0599]: no method named `bar` found for fn item `fn() -> Foo {foo}` in the current scope
|
||||
--> $DIR/call-on-missing.rs:12:9
|
||||
|
|
||||
LL | foo.bar();
|
||||
| ^^^ method not found in `fn() -> Foo {foo}`
|
||||
|
|
||||
help: use parentheses to call this function
|
||||
|
|
||||
LL | foo().bar();
|
||||
| ++
|
||||
|
||||
error[E0609]: no field `i` on type `fn() -> Foo {foo}`
|
||||
--> $DIR/call-on-missing.rs:16:9
|
||||
|
|
||||
LL | foo.i;
|
||||
| ^
|
||||
|
|
||||
help: use parentheses to call this function
|
||||
|
|
||||
LL | foo().i;
|
||||
| ++
|
||||
|
||||
error[E0599]: no method named `bar` found for struct `Box<dyn Fn() -> Foo>` in the current scope
|
||||
--> $DIR/call-on-missing.rs:22:14
|
||||
|
|
||||
LL | callable.bar();
|
||||
| ^^^ method not found in `Box<dyn Fn() -> Foo>`
|
||||
|
|
||||
help: use parentheses to call this trait object
|
||||
|
|
||||
LL | callable().bar();
|
||||
| ++
|
||||
|
||||
error[E0609]: no field `i` on type `Box<dyn Fn() -> Foo>`
|
||||
--> $DIR/call-on-missing.rs:26:14
|
||||
|
|
||||
LL | callable.i;
|
||||
| ^ unknown field
|
||||
|
|
||||
help: use parentheses to call this trait object
|
||||
|
|
||||
LL | callable().i;
|
||||
| ++
|
||||
|
||||
error[E0599]: no method named `bar` found for type parameter `T` in the current scope
|
||||
--> $DIR/call-on-missing.rs:32:7
|
||||
|
|
||||
LL | fn type_param<T: Fn() -> Foo>(t: T) {
|
||||
| - method `bar` not found for this type parameter
|
||||
LL | t.bar();
|
||||
| ^^^ method not found in `T`
|
||||
|
|
||||
help: use parentheses to call this type parameter
|
||||
|
|
||||
LL | t().bar();
|
||||
| ++
|
||||
|
||||
error[E0609]: no field `i` on type `T`
|
||||
--> $DIR/call-on-missing.rs:36:7
|
||||
|
|
||||
LL | fn type_param<T: Fn() -> Foo>(t: T) {
|
||||
| - type parameter 'T' declared here
|
||||
...
|
||||
LL | t.i;
|
||||
| ^
|
||||
|
|
||||
help: use parentheses to call this type parameter
|
||||
|
|
||||
LL | t().i;
|
||||
| ++
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0599, E0609.
|
||||
For more information about an error, try `rustc --explain E0599`.
|
@ -33,8 +33,8 @@ LL | let _: usize = foo;
|
||||
found fn item `fn(usize, usize) -> usize {foo}`
|
||||
help: use parentheses to call this function
|
||||
|
|
||||
LL | let _: usize = foo(_, _);
|
||||
| ++++++
|
||||
LL | let _: usize = foo(/* usize */, /* usize */);
|
||||
| ++++++++++++++++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-or-tuple-struct-without-args.rs:30:16
|
||||
@ -51,8 +51,8 @@ LL | let _: S = S;
|
||||
found fn item `fn(usize, usize) -> S {S}`
|
||||
help: use parentheses to instantiate this tuple struct
|
||||
|
|
||||
LL | let _: S = S(_, _);
|
||||
| ++++++
|
||||
LL | let _: S = S(/* usize */, /* usize */);
|
||||
| ++++++++++++++++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-or-tuple-struct-without-args.rs:31:20
|
||||
@ -103,10 +103,10 @@ LL | let _: usize = T::baz;
|
||||
|
|
||||
= note: expected type `usize`
|
||||
found fn item `fn(usize, usize) -> usize {<_ as T>::baz}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let _: usize = T::baz(_, _);
|
||||
| ++++++
|
||||
LL | let _: usize = T::baz(/* usize */, /* usize */);
|
||||
| ++++++++++++++++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-or-tuple-struct-without-args.rs:34:20
|
||||
@ -121,10 +121,10 @@ LL | let _: usize = T::bat;
|
||||
|
|
||||
= note: expected type `usize`
|
||||
found fn item `fn(usize) -> usize {<_ as T>::bat}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let _: usize = T::bat(_);
|
||||
| +++
|
||||
LL | let _: usize = T::bat(/* usize */);
|
||||
| +++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-or-tuple-struct-without-args.rs:35:16
|
||||
@ -141,8 +141,8 @@ LL | let _: E = E::A;
|
||||
found fn item `fn(usize) -> E {E::A}`
|
||||
help: use parentheses to instantiate this tuple variant
|
||||
|
|
||||
LL | let _: E = E::A(_);
|
||||
| +++
|
||||
LL | let _: E = E::A(/* usize */);
|
||||
| +++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-or-tuple-struct-without-args.rs:37:20
|
||||
@ -157,10 +157,10 @@ LL | let _: usize = X::baz;
|
||||
|
|
||||
= note: expected type `usize`
|
||||
found fn item `fn(usize, usize) -> usize {<X as T>::baz}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let _: usize = X::baz(_, _);
|
||||
| ++++++
|
||||
LL | let _: usize = X::baz(/* usize */, /* usize */);
|
||||
| ++++++++++++++++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-or-tuple-struct-without-args.rs:38:20
|
||||
@ -175,10 +175,10 @@ LL | let _: usize = X::bat;
|
||||
|
|
||||
= note: expected type `usize`
|
||||
found fn item `fn(usize) -> usize {<X as T>::bat}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let _: usize = X::bat(_);
|
||||
| +++
|
||||
LL | let _: usize = X::bat(/* usize */);
|
||||
| +++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-or-tuple-struct-without-args.rs:39:20
|
||||
@ -193,10 +193,10 @@ LL | let _: usize = X::bax;
|
||||
|
|
||||
= note: expected type `usize`
|
||||
found fn item `fn(usize) -> usize {<X as T>::bax}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let _: usize = X::bax(_);
|
||||
| +++
|
||||
LL | let _: usize = X::bax(/* usize */);
|
||||
| +++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-or-tuple-struct-without-args.rs:40:20
|
||||
@ -211,10 +211,10 @@ LL | let _: usize = X::bach;
|
||||
|
|
||||
= note: expected type `usize`
|
||||
found fn item `fn(usize) -> usize {<X as T>::bach}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let _: usize = X::bach(_);
|
||||
| +++
|
||||
LL | let _: usize = X::bach(/* usize */);
|
||||
| +++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-or-tuple-struct-without-args.rs:41:20
|
||||
@ -229,10 +229,10 @@ LL | let _: usize = X::ban;
|
||||
|
|
||||
= note: expected type `usize`
|
||||
found fn item `for<'r> fn(&'r X) -> usize {<X as T>::ban}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let _: usize = X::ban(_);
|
||||
| +++
|
||||
LL | let _: usize = X::ban(/* &X */);
|
||||
| ++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-or-tuple-struct-without-args.rs:42:20
|
||||
@ -247,10 +247,10 @@ LL | let _: usize = X::bal;
|
||||
|
|
||||
= note: expected type `usize`
|
||||
found fn item `for<'r> fn(&'r X) -> usize {<X as T>::bal}`
|
||||
help: use parentheses to call this function
|
||||
help: use parentheses to call this associated function
|
||||
|
|
||||
LL | let _: usize = X::bal(_);
|
||||
| +++
|
||||
LL | let _: usize = X::bal(/* &X */);
|
||||
| ++++++++++
|
||||
|
||||
error[E0615]: attempted to take value of method `ban` on type `X`
|
||||
--> $DIR/fn-or-tuple-struct-without-args.rs:43:22
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0277]: a value of type `i32` cannot be built from an iterator over elements of type `i32`
|
||||
--> $DIR/type-check-defaults.rs:6:19
|
||||
--> $DIR/type-check-defaults.rs:6:23
|
||||
|
|
||||
LL | struct WellFormed<Z = Foo<i32, i32>>(Z);
|
||||
| ^^^^^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
|
||||
| ^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
|
||||
|
|
||||
= help: the trait `FromIterator<i32>` is not implemented for `i32`
|
||||
note: required by a bound in `Foo`
|
||||
@ -12,10 +12,10 @@ LL | struct Foo<T, U: FromIterator<T>>(T, U);
|
||||
| ^^^^^^^^^^^^^^^ required by this bound in `Foo`
|
||||
|
||||
error[E0277]: a value of type `i32` cannot be built from an iterator over elements of type `i32`
|
||||
--> $DIR/type-check-defaults.rs:8:27
|
||||
--> $DIR/type-check-defaults.rs:8:38
|
||||
|
|
||||
LL | struct WellFormedNoBounds<Z:?Sized = Foo<i32, i32>>(Z);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
|
||||
| ^^^^^^^^^^^^^ value of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
|
||||
|
|
||||
= help: the trait `FromIterator<i32>` is not implemented for `i32`
|
||||
note: required by a bound in `Foo`
|
||||
|
@ -2,17 +2,13 @@ error[E0599]: no method named `x` found for fn item `fn() -> Ret {Obj::func}` in
|
||||
--> $DIR/issue-29124.rs:15:15
|
||||
|
|
||||
LL | Obj::func.x();
|
||||
| --------- ^ method not found in `fn() -> Ret {Obj::func}`
|
||||
| |
|
||||
| this is a function, perhaps you wish to call it
|
||||
| ^ method not found in `fn() -> Ret {Obj::func}`
|
||||
|
||||
error[E0599]: no method named `x` found for fn item `fn() -> Ret {func}` in the current scope
|
||||
--> $DIR/issue-29124.rs:17:10
|
||||
|
|
||||
LL | func.x();
|
||||
| ---- ^ method not found in `fn() -> Ret {func}`
|
||||
| |
|
||||
| this is a function, perhaps you wish to call it
|
||||
| ^ method not found in `fn() -> Ret {func}`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -4,7 +4,7 @@ struct Bar<T> {
|
||||
|
||||
struct Foo();
|
||||
impl Foo {
|
||||
fn foo() { }
|
||||
fn foo(&self) { }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -2,11 +2,9 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the
|
||||
--> $DIR/empty-tuple-method.rs:12:15
|
||||
|
|
||||
LL | thing.bar.foo();
|
||||
| --------- ^^^ method not found in `fn() -> Foo {Foo}`
|
||||
| |
|
||||
| this is the constructor of a struct
|
||||
| ^^^ method not found in `fn() -> Foo {Foo}`
|
||||
|
|
||||
help: call the constructor
|
||||
help: use parentheses to instantiate this tuple struct
|
||||
|
|
||||
LL | (thing.bar)().foo();
|
||||
| + +++
|
||||
|
@ -6,7 +6,7 @@ enum Foo{
|
||||
Tup()
|
||||
}
|
||||
impl Foo {
|
||||
fn foo() { }
|
||||
fn foo(&self) { }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -2,11 +2,9 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` i
|
||||
--> $DIR/enum-variant.rs:14:15
|
||||
|
|
||||
LL | thing.bar.foo();
|
||||
| --------- ^^^ method not found in `fn() -> Foo {Foo::Tup}`
|
||||
| |
|
||||
| this is the constructor of an enum variant
|
||||
| ^^^ method not found in `fn() -> Foo {Foo::Tup}`
|
||||
|
|
||||
help: call the constructor
|
||||
help: use parentheses to instantiate this tuple variant
|
||||
|
|
||||
LL | (thing.bar)().foo();
|
||||
| + +++
|
||||
|
@ -2,14 +2,12 @@ error[E0609]: no field `0` on type `fn(char, u16) -> Foo {Foo}`
|
||||
--> $DIR/tuple-field.rs:12:15
|
||||
|
|
||||
LL | thing.bar.0;
|
||||
| --------- ^
|
||||
| |
|
||||
| this is the constructor of a struct
|
||||
| ^
|
||||
|
|
||||
help: call the constructor
|
||||
help: use parentheses to instantiate this tuple struct
|
||||
|
|
||||
LL | (thing.bar)(_, _).0;
|
||||
| + +++++++
|
||||
LL | (thing.bar)(/* char */, /* u16 */).0;
|
||||
| + ++++++++++++++++++++++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -2,14 +2,7 @@ error[E0599]: no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}`
|
||||
--> $DIR/tuple-method.rs:12:15
|
||||
|
|
||||
LL | thing.bar.foo();
|
||||
| --------- ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
|
||||
| |
|
||||
| this is the constructor of a struct
|
||||
|
|
||||
help: call the constructor
|
||||
|
|
||||
LL | (thing.bar)(_, _).foo();
|
||||
| + +++++++
|
||||
| ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -2,27 +2,13 @@ error[E0599]: no method named `nonexistent_method` found for fn item `fn(_) -> O
|
||||
--> $DIR/issue-96738.rs:2:10
|
||||
|
|
||||
LL | Some.nonexistent_method();
|
||||
| ---- ^^^^^^^^^^^^^^^^^^ method not found in `fn(_) -> Option<_> {Option::<_>::Some}`
|
||||
| |
|
||||
| this is the constructor of an enum variant
|
||||
|
|
||||
help: call the constructor
|
||||
|
|
||||
LL | (Some)(_).nonexistent_method();
|
||||
| + ++++
|
||||
| ^^^^^^^^^^^^^^^^^^ method not found in `fn(_) -> Option<_> {Option::<_>::Some}`
|
||||
|
||||
error[E0609]: no field `nonexistent_field` on type `fn(_) -> Option<_> {Option::<_>::Some}`
|
||||
--> $DIR/issue-96738.rs:3:10
|
||||
|
|
||||
LL | Some.nonexistent_field;
|
||||
| ---- ^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| this is the constructor of an enum variant
|
||||
|
|
||||
help: call the constructor
|
||||
|
|
||||
LL | (Some)(_).nonexistent_field;
|
||||
| + ++++
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -2,9 +2,7 @@ error[E0599]: no method named `call` found for closure `[closure@$DIR/unboxed-cl
|
||||
--> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10
|
||||
|
|
||||
LL | mut_.call((0, ));
|
||||
| ---- ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]`
|
||||
| |
|
||||
| this is a function, perhaps you wish to call it
|
||||
| ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0277]: the trait bound `Self: Eq` is not satisfied
|
||||
--> $DIR/wf-trait-fn-ret.rs:10:22
|
||||
--> $DIR/wf-trait-fn-ret.rs:10:23
|
||||
|
|
||||
LL | fn bar(&self) -> &Bar<Self>;
|
||||
| ^^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
||||
| ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
||||
|
|
||||
note: required by a bound in `Bar`
|
||||
--> $DIR/wf-trait-fn-ret.rs:7:14
|
||||
|
Loading…
Reference in New Issue
Block a user