Merge pull request #4532 from rust-lang/rustup-2025-08-20
Automatic Rustup
This commit is contained in:
commit
46765526e3
609 changed files with 15878 additions and 9046 deletions
10
Cargo.lock
10
Cargo.lock
|
|
@ -4812,6 +4812,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"sha2",
|
||||
"smallvec",
|
||||
"stringdex",
|
||||
"tempfile",
|
||||
"threadpool",
|
||||
"tracing",
|
||||
|
|
@ -5225,6 +5226,15 @@ dependencies = [
|
|||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stringdex"
|
||||
version = "0.0.1-alpha4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2841fd43df5b1ff1b042e167068a1fe9b163dc93041eae56ab2296859013a9a0"
|
||||
dependencies = [
|
||||
"stacker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
|
|
|
|||
|
|
@ -12,9 +12,11 @@ attr_parsing_empty_attribute =
|
|||
|
||||
attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target}
|
||||
.help = `#[{$name}]` can {$only}be applied to {$applied}
|
||||
.suggestion = remove the attribute
|
||||
attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target}
|
||||
.warn = {-attr_parsing_previously_accepted}
|
||||
.help = `#[{$name}]` can {$only}be applied to {$applied}
|
||||
.suggestion = remove the attribute
|
||||
|
||||
attr_parsing_empty_confusables =
|
||||
expected at least one confusable name
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
|
|||
Allow(Target::TyAlias),
|
||||
Allow(Target::Use),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::ForeignStatic),
|
||||
Allow(Target::ForeignTy),
|
||||
Allow(Target::Field),
|
||||
Allow(Target::Trait),
|
||||
Allow(Target::AssocTy),
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
|
|||
}
|
||||
}
|
||||
ArgParser::NameValue(_) => {
|
||||
let suggestions =
|
||||
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "inline");
|
||||
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(cx.attr_style, "inline");
|
||||
let span = cx.attr_span;
|
||||
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
|
|||
}
|
||||
}
|
||||
ArgParser::NameValue(_) => {
|
||||
let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use);
|
||||
let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use);
|
||||
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
|
||||
num_suggestions: suggestions.len(),
|
||||
suggestions: DiagArgValue::StrListSepByAnd(
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ pub(crate) mod no_implicit_prelude;
|
|||
pub(crate) mod non_exhaustive;
|
||||
pub(crate) mod path;
|
||||
pub(crate) mod proc_macro_attrs;
|
||||
pub(crate) mod prototype;
|
||||
pub(crate) mod repr;
|
||||
pub(crate) mod rustc_internal;
|
||||
pub(crate) mod semantics;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
use rustc_errors::DiagArgValue;
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::{MethodKind, Target};
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
|
||||
use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage};
|
||||
use crate::context::MaybeWarn::{Allow, Error};
|
||||
use crate::context::{AcceptContext, AllowedTargets, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
pub(crate) struct MustUseParser;
|
||||
|
|
@ -13,7 +15,21 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
|
|||
const PATH: &[Symbol] = &[sym::must_use];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs`
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Union),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::ForeignFn),
|
||||
// `impl Trait` in return position can trip
|
||||
// `unused_must_use` if `Trait` is marked as
|
||||
// `#[must_use]`
|
||||
Allow(Target::Trait),
|
||||
Error(Target::WherePredicate),
|
||||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(
|
||||
Word, NameValueStr: "reason",
|
||||
"https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"
|
||||
|
|
@ -35,8 +51,8 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
|
|||
Some(value_str)
|
||||
}
|
||||
ArgParser::List(_) => {
|
||||
let suggestions =
|
||||
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use");
|
||||
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(cx.attr_style, "must_use");
|
||||
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
|
||||
num_suggestions: suggestions.len(),
|
||||
suggestions: DiagArgValue::StrListSepByAnd(
|
||||
|
|
|
|||
140
compiler/rustc_attr_parsing/src/attributes/prototype.rs
Normal file
140
compiler/rustc_attr_parsing/src/attributes/prototype.rs
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
//! Attributes that are only used on function prototypes.
|
||||
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_hir::Target;
|
||||
use rustc_hir::attrs::{AttributeKind, MirDialect, MirPhase};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::{AttributeOrder, OnDuplicate};
|
||||
use crate::attributes::SingleAttributeParser;
|
||||
use crate::context::{AcceptContext, AllowedTargets, MaybeWarn, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
|
||||
pub(crate) struct CustomMirParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
|
||||
const PATH: &[rustc_span::Symbol] = &[sym::custom_mir];
|
||||
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowList(&[MaybeWarn::Allow(Target::Fn)]);
|
||||
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return None;
|
||||
};
|
||||
|
||||
let mut dialect = None;
|
||||
let mut phase = None;
|
||||
let mut failed = false;
|
||||
|
||||
for item in list.mixed() {
|
||||
let Some(meta_item) = item.meta_item() else {
|
||||
cx.expected_name_value(item.span(), None);
|
||||
failed = true;
|
||||
break;
|
||||
};
|
||||
|
||||
if let Some(arg) = meta_item.word_is(sym::dialect) {
|
||||
extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed);
|
||||
} else if let Some(arg) = meta_item.word_is(sym::phase) {
|
||||
extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed);
|
||||
} else if let Some(word) = meta_item.path().word() {
|
||||
let word = word.to_string();
|
||||
cx.unknown_key(meta_item.span(), word, &["dialect", "phase"]);
|
||||
failed = true;
|
||||
} else {
|
||||
cx.expected_name_value(meta_item.span(), None);
|
||||
failed = true;
|
||||
};
|
||||
}
|
||||
|
||||
let dialect = parse_dialect(cx, dialect, &mut failed);
|
||||
let phase = parse_phase(cx, phase, &mut failed);
|
||||
|
||||
if failed {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(AttributeKind::CustomMir(dialect, phase, cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_value<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
key: Symbol,
|
||||
arg: &ArgParser<'_>,
|
||||
span: Span,
|
||||
out_val: &mut Option<(Symbol, Span)>,
|
||||
failed: &mut bool,
|
||||
) {
|
||||
if out_val.is_some() {
|
||||
cx.duplicate_key(span, key);
|
||||
*failed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(val) = arg.name_value() else {
|
||||
cx.expected_single_argument(arg.span().unwrap_or(span));
|
||||
*failed = true;
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(value_sym) = val.value_as_str() else {
|
||||
cx.expected_string_literal(val.value_span, Some(val.value_as_lit()));
|
||||
*failed = true;
|
||||
return;
|
||||
};
|
||||
|
||||
*out_val = Some((value_sym, val.value_span));
|
||||
}
|
||||
|
||||
fn parse_dialect<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
dialect: Option<(Symbol, Span)>,
|
||||
failed: &mut bool,
|
||||
) -> Option<(MirDialect, Span)> {
|
||||
let (dialect, span) = dialect?;
|
||||
|
||||
let dialect = match dialect {
|
||||
sym::analysis => MirDialect::Analysis,
|
||||
sym::built => MirDialect::Built,
|
||||
sym::runtime => MirDialect::Runtime,
|
||||
|
||||
_ => {
|
||||
cx.expected_specific_argument(span, vec!["analysis", "built", "runtime"]);
|
||||
*failed = true;
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some((dialect, span))
|
||||
}
|
||||
|
||||
fn parse_phase<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
phase: Option<(Symbol, Span)>,
|
||||
failed: &mut bool,
|
||||
) -> Option<(MirPhase, Span)> {
|
||||
let (phase, span) = phase?;
|
||||
|
||||
let phase = match phase {
|
||||
sym::initial => MirPhase::Initial,
|
||||
sym::post_cleanup => MirPhase::PostCleanup,
|
||||
sym::optimized => MirPhase::Optimized,
|
||||
|
||||
_ => {
|
||||
cx.expected_specific_argument(span, vec!["initial", "post-cleanup", "optimized"]);
|
||||
*failed = true;
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some((phase, span))
|
||||
}
|
||||
|
|
@ -54,6 +54,7 @@ const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
|||
Allow(Target::Static),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::ForeignStatic),
|
||||
Allow(Target::ExternCrate),
|
||||
]);
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
|
|||
ArgParser::NameValue(name_value) => {
|
||||
let Some(str_value) = name_value.value_as_str() else {
|
||||
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(false, "ignore");
|
||||
.suggestions(cx.attr_style, "ignore");
|
||||
let span = cx.attr_span;
|
||||
cx.emit_lint(
|
||||
AttributeLintKind::IllFormedAttributeInput { suggestions },
|
||||
|
|
@ -40,8 +40,8 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
|
|||
Some(str_value)
|
||||
}
|
||||
ArgParser::List(_) => {
|
||||
let suggestions =
|
||||
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "ignore");
|
||||
let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
|
||||
.suggestions(cx.attr_style, "ignore");
|
||||
let span = cx.attr_span;
|
||||
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::sync::LazyLock;
|
|||
|
||||
use itertools::Itertools;
|
||||
use private::Sealed;
|
||||
use rustc_ast::{self as ast, LitKind, MetaItemLit, NodeId};
|
||||
use rustc_ast::{self as ast, AttrStyle, LitKind, MetaItemLit, NodeId};
|
||||
use rustc_errors::{DiagCtxtHandle, Diagnostic};
|
||||
use rustc_feature::{AttributeTemplate, Features};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
|
|
@ -46,6 +46,7 @@ use crate::attributes::path::PathParser as PathAttributeParser;
|
|||
use crate::attributes::proc_macro_attrs::{
|
||||
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
|
||||
};
|
||||
use crate::attributes::prototype::CustomMirParser;
|
||||
use crate::attributes::repr::{AlignParser, ReprParser};
|
||||
use crate::attributes::rustc_internal::{
|
||||
RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
|
||||
|
|
@ -167,6 +168,7 @@ attribute_parsers!(
|
|||
|
||||
// tidy-alphabetical-start
|
||||
Single<CoverageParser>,
|
||||
Single<CustomMirParser>,
|
||||
Single<DeprecationParser>,
|
||||
Single<DummyParser>,
|
||||
Single<ExportNameParser>,
|
||||
|
|
@ -313,6 +315,7 @@ pub struct AcceptContext<'f, 'sess, S: Stage> {
|
|||
/// The span of the attribute currently being parsed
|
||||
pub(crate) attr_span: Span,
|
||||
|
||||
pub(crate) attr_style: AttrStyle,
|
||||
/// The expected structure of the attribute.
|
||||
///
|
||||
/// Used in reporting errors to give a hint to users what the attribute *should* look like.
|
||||
|
|
@ -394,6 +397,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
|
||||
}),
|
||||
},
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -404,6 +408,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedIntegerLiteral,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -414,6 +419,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedList,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -424,6 +430,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedNoArgs,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -435,6 +442,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedIdentifier,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -447,6 +455,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedNameValue(name),
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -458,6 +467,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::DuplicateKey(key),
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -470,6 +480,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::UnexpectedLiteral,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -480,6 +491,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedSingleArgument,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -490,6 +502,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument,
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -508,6 +521,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
strings: false,
|
||||
list: false,
|
||||
},
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -526,6 +540,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
strings: false,
|
||||
list: true,
|
||||
},
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -544,6 +559,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
strings: true,
|
||||
list: false,
|
||||
},
|
||||
attr_style: self.attr_style,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -802,6 +818,7 @@ impl<'sess> AttributeParser<'sess, Early> {
|
|||
},
|
||||
},
|
||||
attr_span: attr.span,
|
||||
attr_style: attr.style,
|
||||
template,
|
||||
attr_path: path.get_attribute_path(),
|
||||
};
|
||||
|
|
@ -912,6 +929,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
|||
emit_lint: &mut emit_lint,
|
||||
},
|
||||
attr_span: lower_span(attr.span),
|
||||
attr_style: attr.style,
|
||||
template: &accept.template,
|
||||
attr_path: path.get_attribute_path(),
|
||||
};
|
||||
|
|
@ -1060,6 +1078,9 @@ pub(crate) fn allowed_targets_applied(
|
|||
if !features.stmt_expr_attributes() {
|
||||
allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement));
|
||||
}
|
||||
if !features.extern_types() {
|
||||
allowed_targets.retain(|t| !matches!(t, Target::ForeignTy));
|
||||
}
|
||||
}
|
||||
|
||||
// We define groups of "similar" targets.
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emi
|
|||
target: target.plural_name(),
|
||||
applied: applied.clone(),
|
||||
only,
|
||||
attr_span: *span,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::num::IntErrorKind;
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::{self as ast, AttrStyle};
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{
|
||||
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
|
||||
|
|
@ -489,6 +489,8 @@ pub(crate) struct InvalidTargetLint {
|
|||
pub target: &'static str,
|
||||
pub applied: String,
|
||||
pub only: &'static str,
|
||||
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
|
||||
pub attr_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -496,6 +498,7 @@ pub(crate) struct InvalidTargetLint {
|
|||
#[diag(attr_parsing_invalid_target)]
|
||||
pub(crate) struct InvalidTarget {
|
||||
#[primary_span]
|
||||
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
|
||||
pub span: Span,
|
||||
pub name: Symbol,
|
||||
pub target: &'static str,
|
||||
|
|
@ -579,6 +582,7 @@ pub(crate) enum AttributeParseErrorReason {
|
|||
pub(crate) struct AttributeParseError {
|
||||
pub(crate) span: Span,
|
||||
pub(crate) attr_span: Span,
|
||||
pub(crate) attr_style: AttrStyle,
|
||||
pub(crate) template: AttributeTemplate,
|
||||
pub(crate) attribute: AttrPath,
|
||||
pub(crate) reason: AttributeParseErrorReason,
|
||||
|
|
@ -717,7 +721,8 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
|
|||
if let Some(link) = self.template.docs {
|
||||
diag.note(format!("for more information, visit <{link}>"));
|
||||
}
|
||||
let suggestions = self.template.suggestions(false, &name);
|
||||
let suggestions = self.template.suggestions(self.attr_style, &name);
|
||||
|
||||
diag.span_suggestions(
|
||||
self.attr_span,
|
||||
if suggestions.len() == 1 {
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ borrowck_lifetime_constraints_error =
|
|||
lifetime may not live long enough
|
||||
|
||||
borrowck_limitations_implies_static =
|
||||
due to current limitations in the borrow checker, this implies a `'static` lifetime
|
||||
due to a current limitation of the type system, this implies a `'static` lifetime
|
||||
|
||||
borrowck_move_closure_suggestion =
|
||||
consider adding 'move' keyword before the nested closure
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ use rustc_abi::{FieldIdx, VariantIdx};
|
|||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
|
||||
use rustc_hir::def::{CtorKind, Namespace};
|
||||
use rustc_hir::{self as hir, CoroutineKind, LangItem};
|
||||
use rustc_hir::{
|
||||
self as hir, CoroutineKind, GenericBound, LangItem, WhereBoundPredicate, WherePredicateKind,
|
||||
};
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
|
||||
use rustc_infer::traits::SelectionError;
|
||||
|
|
@ -658,25 +660,66 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
|
||||
/// Add a note to region errors and borrow explanations when higher-ranked regions in predicates
|
||||
/// implicitly introduce an "outlives `'static`" constraint.
|
||||
///
|
||||
/// This is very similar to `fn suggest_static_lifetime_for_gat_from_hrtb` which handles this
|
||||
/// note for failed type tests instead of outlives errors.
|
||||
fn add_placeholder_from_predicate_note<G: EmissionGuarantee>(
|
||||
&self,
|
||||
err: &mut Diag<'_, G>,
|
||||
diag: &mut Diag<'_, G>,
|
||||
path: &[OutlivesConstraint<'tcx>],
|
||||
) {
|
||||
let predicate_span = path.iter().find_map(|constraint| {
|
||||
let tcx = self.infcx.tcx;
|
||||
let Some((gat_hir_id, generics)) = path.iter().find_map(|constraint| {
|
||||
let outlived = constraint.sub;
|
||||
if let Some(origin) = self.regioncx.definitions.get(outlived)
|
||||
&& let NllRegionVariableOrigin::Placeholder(_) = origin.origin
|
||||
&& let ConstraintCategory::Predicate(span) = constraint.category
|
||||
&& let NllRegionVariableOrigin::Placeholder(placeholder) = origin.origin
|
||||
&& let Some(id) = placeholder.bound.kind.get_id()
|
||||
&& let Some(placeholder_id) = id.as_local()
|
||||
&& let gat_hir_id = tcx.local_def_id_to_hir_id(placeholder_id)
|
||||
&& let Some(generics_impl) =
|
||||
tcx.parent_hir_node(tcx.parent_hir_id(gat_hir_id)).generics()
|
||||
{
|
||||
Some(span)
|
||||
Some((gat_hir_id, generics_impl))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(span) = predicate_span {
|
||||
err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
|
||||
// Look for the where-bound which introduces the placeholder.
|
||||
// As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>`
|
||||
// and `T: for<'a> Trait`<'a>.
|
||||
for pred in generics.predicates {
|
||||
let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
|
||||
bound_generic_params,
|
||||
bounds,
|
||||
..
|
||||
}) = pred.kind
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if bound_generic_params
|
||||
.iter()
|
||||
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
|
||||
.is_some()
|
||||
{
|
||||
diag.span_note(pred.span, fluent::borrowck_limitations_implies_static);
|
||||
return;
|
||||
}
|
||||
for bound in bounds.iter() {
|
||||
if let GenericBound::Trait(bound) = bound {
|
||||
if bound
|
||||
.bound_generic_params
|
||||
.iter()
|
||||
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
|
||||
.is_some()
|
||||
{
|
||||
diag.span_note(bound.span, fluent::borrowck_limitations_implies_static);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use either::Either;
|
||||
use hir::{ExprKind, Param};
|
||||
use rustc_abi::FieldIdx;
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
|
|
@ -12,15 +13,16 @@ use rustc_middle::bug;
|
|||
use rustc_middle::hir::place::PlaceBase;
|
||||
use rustc_middle::mir::visit::PlaceContext;
|
||||
use rustc_middle::mir::{
|
||||
self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location, Mutability, Place,
|
||||
PlaceRef, ProjectionElem,
|
||||
self, BindingForm, Body, BorrowKind, Local, LocalDecl, LocalInfo, LocalKind, Location,
|
||||
Mutability, Operand, Place, PlaceRef, ProjectionElem, RawPtrKind, Rvalue, Statement,
|
||||
StatementKind, TerminatorKind,
|
||||
};
|
||||
use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Upcast};
|
||||
use rustc_span::{BytePos, DesugaringKind, Span, Symbol, kw, sym};
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits;
|
||||
use tracing::debug;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::diagnostics::BorrowedContentSource;
|
||||
use crate::{MirBorrowckCtxt, session_diagnostics};
|
||||
|
|
@ -31,6 +33,33 @@ pub(crate) enum AccessKind {
|
|||
Mutate,
|
||||
}
|
||||
|
||||
/// Finds all statements that assign directly to local (i.e., X = ...) and returns their
|
||||
/// locations.
|
||||
fn find_assignments(body: &Body<'_>, local: Local) -> Vec<Location> {
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
|
||||
struct FindLocalAssignmentVisitor {
|
||||
needle: Local,
|
||||
locations: Vec<Location>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
|
||||
fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) {
|
||||
if self.needle != local {
|
||||
return;
|
||||
}
|
||||
|
||||
if place_context.is_place_assignment() {
|
||||
self.locations.push(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
|
||||
visitor.visit_body(body);
|
||||
visitor.locations
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
pub(crate) fn report_mutability_error(
|
||||
&mut self,
|
||||
|
|
@ -384,7 +413,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// Also suggest adding mut for upvars
|
||||
// Also suggest adding mut for upvars.
|
||||
PlaceRef {
|
||||
local,
|
||||
projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
|
||||
|
|
@ -438,9 +467,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// complete hack to approximate old AST-borrowck
|
||||
// diagnostic: if the span starts with a mutable borrow of
|
||||
// a local variable, then just suggest the user remove it.
|
||||
// Complete hack to approximate old AST-borrowck diagnostic: if the span starts
|
||||
// with a mutable borrow of a local variable, then just suggest the user remove it.
|
||||
PlaceRef { local: _, projection: [] }
|
||||
if self
|
||||
.infcx
|
||||
|
|
@ -769,7 +797,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
// point to span of upvar making closure call require mutable borrow
|
||||
// Point to span of upvar making closure call that requires a mutable borrow
|
||||
fn show_mutating_upvar(
|
||||
&self,
|
||||
tcx: TyCtxt<'_>,
|
||||
|
|
@ -825,7 +853,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
} else {
|
||||
bug!("not an upvar")
|
||||
};
|
||||
// sometimes we deliberately don't store the name of a place when coming from a macro in
|
||||
// Sometimes we deliberately don't store the name of a place when coming from a macro in
|
||||
// another crate. We generally want to limit those diagnostics a little, to hide
|
||||
// implementation details (such as those from pin!() or format!()). In that case show a
|
||||
// slightly different error message, or none at all if something else happened. In other
|
||||
|
|
@ -936,8 +964,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let def_id = tcx.hir_enclosing_body_owner(fn_call_id);
|
||||
let mut look_at_return = true;
|
||||
|
||||
// If the HIR node is a function or method call gets the def ID
|
||||
// of the called function or method and the span and args of the call expr
|
||||
// If the HIR node is a function or method call, get the DefId
|
||||
// of the callee function or method, the span, and args of the call expr
|
||||
let get_call_details = || {
|
||||
let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else {
|
||||
return None;
|
||||
|
|
@ -1051,7 +1079,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let mut cur_expr = expr;
|
||||
while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
|
||||
if path_segment.ident.name == sym::iter {
|
||||
// check `_ty` has `iter_mut` method
|
||||
// Check that the type has an `iter_mut` method.
|
||||
let res = self
|
||||
.infcx
|
||||
.tcx
|
||||
|
|
@ -1081,38 +1109,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Finds all statements that assign directly to local (i.e., X = ...) and returns their
|
||||
/// locations.
|
||||
fn find_assignments(&self, local: Local) -> Vec<Location> {
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
|
||||
struct FindLocalAssignmentVisitor {
|
||||
needle: Local,
|
||||
locations: Vec<Location>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
|
||||
fn visit_local(
|
||||
&mut self,
|
||||
local: Local,
|
||||
place_context: PlaceContext,
|
||||
location: Location,
|
||||
) {
|
||||
if self.needle != local {
|
||||
return;
|
||||
}
|
||||
|
||||
if place_context.is_place_assignment() {
|
||||
self.locations.push(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
|
||||
visitor.visit_body(self.body);
|
||||
visitor.locations
|
||||
}
|
||||
|
||||
fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) {
|
||||
let local_decl = &self.body.local_decls[local];
|
||||
|
||||
|
|
@ -1122,7 +1118,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local);
|
||||
|
||||
if is_trait_sig && !is_local {
|
||||
// Do not suggest to change the signature when the trait comes from another crate.
|
||||
// Do not suggest changing the signature when the trait comes from another crate.
|
||||
err.span_label(
|
||||
local_decl.source_info.span,
|
||||
format!("this is an immutable {pointer_desc}"),
|
||||
|
|
@ -1131,11 +1127,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
let decl_span = local_decl.source_info.span;
|
||||
|
||||
let amp_mut_sugg = match *local_decl.local_info() {
|
||||
let (amp_mut_sugg, local_var_ty_info) = match *local_decl.local_info() {
|
||||
LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
|
||||
let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
|
||||
let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span));
|
||||
Some(AmpMutSugg { has_sugg: true, span, suggestion, additional })
|
||||
(AmpMutSugg::Type { span, suggestion, additional }, None)
|
||||
}
|
||||
|
||||
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
|
||||
|
|
@ -1143,79 +1139,54 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
opt_ty_info,
|
||||
..
|
||||
})) => {
|
||||
// check if the RHS is from desugaring
|
||||
// Check if the RHS is from desugaring.
|
||||
let first_assignment = find_assignments(&self.body, local).first().copied();
|
||||
let first_assignment_stmt = first_assignment
|
||||
.and_then(|loc| self.body[loc.block].statements.get(loc.statement_index));
|
||||
trace!(?first_assignment_stmt);
|
||||
let opt_assignment_rhs_span =
|
||||
self.find_assignments(local).first().map(|&location| {
|
||||
if let Some(mir::Statement {
|
||||
source_info: _,
|
||||
kind:
|
||||
mir::StatementKind::Assign(box (
|
||||
_,
|
||||
mir::Rvalue::Use(mir::Operand::Copy(place)),
|
||||
)),
|
||||
..
|
||||
}) = self.body[location.block].statements.get(location.statement_index)
|
||||
{
|
||||
self.body.local_decls[place.local].source_info.span
|
||||
} else {
|
||||
self.body.source_info(location).span
|
||||
}
|
||||
});
|
||||
match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
|
||||
// on for loops, RHS points to the iterator part
|
||||
Some(DesugaringKind::ForLoop) => {
|
||||
let span = opt_assignment_rhs_span.unwrap();
|
||||
self.suggest_similar_mut_method_for_for_loop(err, span);
|
||||
first_assignment.map(|loc| self.body.source_info(loc).span);
|
||||
let mut source_span = opt_assignment_rhs_span;
|
||||
if let Some(mir::Statement {
|
||||
source_info: _,
|
||||
kind:
|
||||
mir::StatementKind::Assign(box (_, mir::Rvalue::Use(mir::Operand::Copy(place)))),
|
||||
..
|
||||
}) = first_assignment_stmt
|
||||
{
|
||||
let local_span = self.body.local_decls[place.local].source_info.span;
|
||||
// `&self` in async functions have a `desugaring_kind`, but the local we assign
|
||||
// it with does not, so use the local_span for our checks later.
|
||||
source_span = Some(local_span);
|
||||
if let Some(DesugaringKind::ForLoop) = local_span.desugaring_kind() {
|
||||
// On for loops, RHS points to the iterator part.
|
||||
self.suggest_similar_mut_method_for_for_loop(err, local_span);
|
||||
err.span_label(
|
||||
span,
|
||||
local_span,
|
||||
format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
|
||||
);
|
||||
None
|
||||
}
|
||||
// don't create labels for compiler-generated spans
|
||||
Some(_) => None,
|
||||
// don't create labels for the span not from user's code
|
||||
None if opt_assignment_rhs_span
|
||||
.is_some_and(|span| self.infcx.tcx.sess.source_map().is_imported(span)) =>
|
||||
{
|
||||
None
|
||||
}
|
||||
None => {
|
||||
if name != kw::SelfLower {
|
||||
suggest_ampmut(
|
||||
self.infcx.tcx,
|
||||
local_decl.ty,
|
||||
decl_span,
|
||||
opt_assignment_rhs_span,
|
||||
opt_ty_info,
|
||||
)
|
||||
} else {
|
||||
match local_decl.local_info() {
|
||||
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
|
||||
opt_ty_info: None,
|
||||
..
|
||||
})) => {
|
||||
let (span, sugg) =
|
||||
suggest_ampmut_self(self.infcx.tcx, decl_span);
|
||||
Some(AmpMutSugg {
|
||||
has_sugg: true,
|
||||
span,
|
||||
suggestion: sugg,
|
||||
additional: None,
|
||||
})
|
||||
}
|
||||
// explicit self (eg `self: &'a Self`)
|
||||
_ => suggest_ampmut(
|
||||
self.infcx.tcx,
|
||||
local_decl.ty,
|
||||
decl_span,
|
||||
opt_assignment_rhs_span,
|
||||
opt_ty_info,
|
||||
),
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't create labels for compiler-generated spans or spans not from users' code.
|
||||
if source_span.is_some_and(|s| {
|
||||
s.desugaring_kind().is_some() || self.infcx.tcx.sess.source_map().is_imported(s)
|
||||
}) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This could be because we're in an `async fn`.
|
||||
if name == kw::SelfLower && opt_ty_info.is_none() {
|
||||
let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
|
||||
(AmpMutSugg::Type { span, suggestion, additional: None }, None)
|
||||
} else if let Some(sugg) =
|
||||
suggest_ampmut(self.infcx, self.body(), first_assignment_stmt)
|
||||
{
|
||||
(sugg, opt_ty_info)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
|
||||
|
|
@ -1223,181 +1194,238 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
..
|
||||
})) => {
|
||||
let pattern_span: Span = local_decl.source_info.span;
|
||||
suggest_ref_mut(self.infcx.tcx, pattern_span).map(|span| AmpMutSugg {
|
||||
has_sugg: true,
|
||||
span,
|
||||
suggestion: "mut ".to_owned(),
|
||||
additional: None,
|
||||
})
|
||||
let Some(span) = suggest_ref_mut(self.infcx.tcx, pattern_span) else {
|
||||
return;
|
||||
};
|
||||
(AmpMutSugg::Type { span, suggestion: "mut ".to_owned(), additional: None }, None)
|
||||
}
|
||||
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match amp_mut_sugg {
|
||||
Some(AmpMutSugg {
|
||||
has_sugg: true,
|
||||
span: err_help_span,
|
||||
suggestion: suggested_code,
|
||||
additional,
|
||||
}) => {
|
||||
let mut sugg = vec![(err_help_span, suggested_code)];
|
||||
if let Some(s) = additional {
|
||||
sugg.push(s);
|
||||
}
|
||||
let mut suggest = |suggs: Vec<_>, applicability, extra| {
|
||||
if suggs.iter().any(|(span, _)| self.infcx.tcx.sess.source_map().is_imported(*span)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if sugg.iter().all(|(span, _)| !self.infcx.tcx.sess.source_map().is_imported(*span))
|
||||
{
|
||||
err.multipart_suggestion_verbose(
|
||||
format!(
|
||||
"consider changing this to be a mutable {pointer_desc}{}",
|
||||
if is_trait_sig {
|
||||
" in the `impl` method and the `trait` definition"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
),
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
err.multipart_suggestion_verbose(
|
||||
format!(
|
||||
"consider changing this to be a mutable {pointer_desc}{}{extra}",
|
||||
if is_trait_sig {
|
||||
" in the `impl` method and the `trait` definition"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
),
|
||||
suggs,
|
||||
applicability,
|
||||
);
|
||||
};
|
||||
|
||||
let (mut sugg, add_type_annotation_if_not_exists) = match amp_mut_sugg {
|
||||
AmpMutSugg::Type { span, suggestion, additional } => {
|
||||
let mut sugg = vec![(span, suggestion)];
|
||||
sugg.extend(additional);
|
||||
suggest(sugg, Applicability::MachineApplicable, "");
|
||||
return;
|
||||
}
|
||||
AmpMutSugg::MapGetMut { span, suggestion } => {
|
||||
if self.infcx.tcx.sess.source_map().is_imported(span) {
|
||||
return;
|
||||
}
|
||||
err.multipart_suggestion_verbose(
|
||||
"consider using `get_mut`",
|
||||
vec![(span, suggestion)],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return;
|
||||
}
|
||||
AmpMutSugg::Expr { span, suggestion } => {
|
||||
// `Expr` suggestions should change type annotations if they already exist (probably immut),
|
||||
// but do not add new type annotations.
|
||||
(vec![(span, suggestion)], false)
|
||||
}
|
||||
AmpMutSugg::ChangeBinding => (vec![], true),
|
||||
};
|
||||
|
||||
// Find a binding's type to make mutable.
|
||||
let (binding_exists, span) = match local_var_ty_info {
|
||||
// If this is a variable binding with an explicit type,
|
||||
// then we will suggest changing it to be mutable.
|
||||
// This is `Applicability::MachineApplicable`.
|
||||
Some(ty_span) => (true, ty_span),
|
||||
|
||||
// Otherwise, we'll suggest *adding* an annotated type, we'll suggest
|
||||
// the RHS's type for that.
|
||||
// This is `Applicability::HasPlaceholders`.
|
||||
None => (false, decl_span),
|
||||
};
|
||||
|
||||
if !binding_exists && !add_type_annotation_if_not_exists {
|
||||
suggest(sugg, Applicability::MachineApplicable, "");
|
||||
return;
|
||||
}
|
||||
|
||||
// If the binding already exists and is a reference with an explicit
|
||||
// lifetime, then we can suggest adding ` mut`. This is special-cased from
|
||||
// the path without an explicit lifetime.
|
||||
let (sugg_span, sugg_str, suggest_now) = if let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span)
|
||||
&& src.starts_with("&'")
|
||||
// Note that `&' a T` is invalid so this is correct.
|
||||
&& let Some(ws_pos) = src.find(char::is_whitespace)
|
||||
{
|
||||
let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
|
||||
(span, " mut".to_owned(), true)
|
||||
// If there is already a binding, we modify it to be `mut`.
|
||||
} else if binding_exists {
|
||||
// Shrink the span to just after the `&` in `&variable`.
|
||||
let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
|
||||
(span, "mut ".to_owned(), true)
|
||||
} else {
|
||||
// Otherwise, suggest that the user annotates the binding; We provide the
|
||||
// type of the local.
|
||||
let ty = local_decl.ty.builtin_deref(true).unwrap();
|
||||
|
||||
(span, format!("{}mut {}", if local_decl.ty.is_ref() { "&" } else { "*" }, ty), false)
|
||||
};
|
||||
|
||||
if suggest_now {
|
||||
// Suggest changing `&x` to `&mut x` and changing `&T` to `&mut T` at the same time.
|
||||
let has_change = !sugg.is_empty();
|
||||
sugg.push((sugg_span, sugg_str));
|
||||
suggest(
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
// FIXME(fee1-dead) this somehow doesn't fire
|
||||
if has_change { " and changing the binding's type" } else { "" },
|
||||
);
|
||||
return;
|
||||
} else if !sugg.is_empty() {
|
||||
suggest(sugg, Applicability::MachineApplicable, "");
|
||||
return;
|
||||
}
|
||||
|
||||
let def_id = self.body.source.def_id();
|
||||
let hir_id = if let Some(local_def_id) = def_id.as_local()
|
||||
&& let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
|
||||
{
|
||||
BindingFinder { span: sugg_span }.visit_body(&body).break_value()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let node = hir_id.map(|hir_id| self.infcx.tcx.hir_node(hir_id));
|
||||
|
||||
let Some(hir::Node::LetStmt(local)) = node else {
|
||||
err.span_label(
|
||||
sugg_span,
|
||||
format!("consider changing this binding's type to be: `{sugg_str}`"),
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
|
||||
if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
|
||||
&& let Some(expr) = local.init
|
||||
&& let ty = tables.node_type_opt(expr.hir_id)
|
||||
&& let Some(ty) = ty
|
||||
&& let ty::Ref(..) = ty.kind()
|
||||
{
|
||||
match self
|
||||
.infcx
|
||||
.type_implements_trait_shallow(clone_trait, ty.peel_refs(), self.infcx.param_env)
|
||||
.as_deref()
|
||||
{
|
||||
Some([]) => {
|
||||
// FIXME: This error message isn't useful, since we're just
|
||||
// vaguely suggesting to clone a value that already
|
||||
// implements `Clone`.
|
||||
//
|
||||
// A correct suggestion here would take into account the fact
|
||||
// that inference may be affected by missing types on bindings,
|
||||
// etc., to improve "tests/ui/borrowck/issue-91206.stderr", for
|
||||
// example.
|
||||
}
|
||||
None => {
|
||||
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
|
||||
&& segment.ident.name == sym::clone
|
||||
{
|
||||
err.span_help(
|
||||
span,
|
||||
format!(
|
||||
"`{}` doesn't implement `Clone`, so this call clones \
|
||||
the reference `{ty}`",
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
}
|
||||
// The type doesn't implement Clone.
|
||||
let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
|
||||
self.infcx.tcx,
|
||||
clone_trait,
|
||||
[ty.peel_refs()],
|
||||
));
|
||||
let obligation = traits::Obligation::new(
|
||||
self.infcx.tcx,
|
||||
traits::ObligationCause::dummy(),
|
||||
self.infcx.param_env,
|
||||
trait_ref,
|
||||
);
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&obligation,
|
||||
err,
|
||||
trait_ref.upcast(self.infcx.tcx),
|
||||
);
|
||||
}
|
||||
}
|
||||
Some(AmpMutSugg {
|
||||
has_sugg: false, span: err_label_span, suggestion: message, ..
|
||||
}) => {
|
||||
let def_id = self.body.source.def_id();
|
||||
let hir_id = if let Some(local_def_id) = def_id.as_local()
|
||||
&& let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
|
||||
{
|
||||
BindingFinder { span: err_label_span }.visit_body(&body).break_value()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(hir_id) = hir_id
|
||||
&& let hir::Node::LetStmt(local) = self.infcx.tcx.hir_node(hir_id)
|
||||
{
|
||||
let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
|
||||
if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
|
||||
&& let Some(expr) = local.init
|
||||
&& let ty = tables.node_type_opt(expr.hir_id)
|
||||
&& let Some(ty) = ty
|
||||
&& let ty::Ref(..) = ty.kind()
|
||||
Some(errors) => {
|
||||
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
|
||||
&& segment.ident.name == sym::clone
|
||||
{
|
||||
match self
|
||||
.infcx
|
||||
.type_implements_trait_shallow(
|
||||
clone_trait,
|
||||
ty.peel_refs(),
|
||||
self.infcx.param_env,
|
||||
)
|
||||
.as_deref()
|
||||
{
|
||||
Some([]) => {
|
||||
// FIXME: This error message isn't useful, since we're just
|
||||
// vaguely suggesting to clone a value that already
|
||||
// implements `Clone`.
|
||||
//
|
||||
// A correct suggestion here would take into account the fact
|
||||
// that inference may be affected by missing types on bindings,
|
||||
// etc., to improve "tests/ui/borrowck/issue-91206.stderr", for
|
||||
// example.
|
||||
}
|
||||
None => {
|
||||
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
|
||||
expr.kind
|
||||
&& segment.ident.name == sym::clone
|
||||
{
|
||||
err.span_help(
|
||||
span,
|
||||
format!(
|
||||
"`{}` doesn't implement `Clone`, so this call clones \
|
||||
the reference `{ty}`",
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
}
|
||||
// The type doesn't implement Clone.
|
||||
let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
|
||||
self.infcx.tcx,
|
||||
clone_trait,
|
||||
[ty.peel_refs()],
|
||||
));
|
||||
let obligation = traits::Obligation::new(
|
||||
self.infcx.tcx,
|
||||
traits::ObligationCause::dummy(),
|
||||
self.infcx.param_env,
|
||||
trait_ref,
|
||||
);
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&obligation,
|
||||
err,
|
||||
trait_ref.upcast(self.infcx.tcx),
|
||||
);
|
||||
}
|
||||
Some(errors) => {
|
||||
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
|
||||
expr.kind
|
||||
&& segment.ident.name == sym::clone
|
||||
{
|
||||
err.span_help(
|
||||
span,
|
||||
format!(
|
||||
"`{}` doesn't implement `Clone` because its \
|
||||
err.span_help(
|
||||
span,
|
||||
format!(
|
||||
"`{}` doesn't implement `Clone` because its \
|
||||
implementations trait bounds could not be met, so \
|
||||
this call clones the reference `{ty}`",
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
err.note(format!(
|
||||
"the following trait bounds weren't met: {}",
|
||||
errors
|
||||
.iter()
|
||||
.map(|e| e.obligation.predicate.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
));
|
||||
}
|
||||
// The type doesn't implement Clone because of unmet obligations.
|
||||
for error in errors {
|
||||
if let traits::FulfillmentErrorCode::Select(
|
||||
traits::SelectionError::Unimplemented,
|
||||
) = error.code
|
||||
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
|
||||
pred,
|
||||
)) = error.obligation.predicate.kind().skip_binder()
|
||||
{
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&error.obligation,
|
||||
err,
|
||||
error.obligation.predicate.kind().rebind(pred),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
err.note(format!(
|
||||
"the following trait bounds weren't met: {}",
|
||||
errors
|
||||
.iter()
|
||||
.map(|e| e.obligation.predicate.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
));
|
||||
}
|
||||
// The type doesn't implement Clone because of unmet obligations.
|
||||
for error in errors {
|
||||
if let traits::FulfillmentErrorCode::Select(
|
||||
traits::SelectionError::Unimplemented,
|
||||
) = error.code
|
||||
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
|
||||
error.obligation.predicate.kind().skip_binder()
|
||||
{
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&error.obligation,
|
||||
err,
|
||||
error.obligation.predicate.kind().rebind(pred),
|
||||
);
|
||||
}
|
||||
}
|
||||
let (changing, span, sugg) = match local.ty {
|
||||
Some(ty) => ("changing", ty.span, message),
|
||||
None => {
|
||||
("specifying", local.pat.span.shrink_to_hi(), format!(": {message}"))
|
||||
}
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
format!("consider {changing} this binding's type"),
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else {
|
||||
err.span_label(
|
||||
err_label_span,
|
||||
format!("consider changing this binding's type to be: `{message}`"),
|
||||
);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
let (changing, span, sugg) = match local.ty {
|
||||
Some(ty) => ("changing", ty.span, sugg_str),
|
||||
None => ("specifying", local.pat.span.shrink_to_hi(), format!(": {sugg_str}")),
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
format!("consider {changing} this binding's type"),
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1464,11 +1492,25 @@ fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) {
|
|||
}
|
||||
}
|
||||
|
||||
struct AmpMutSugg {
|
||||
has_sugg: bool,
|
||||
span: Span,
|
||||
suggestion: String,
|
||||
additional: Option<(Span, String)>,
|
||||
enum AmpMutSugg {
|
||||
/// Type suggestion. Changes `&self` to `&mut self`, `x: &T` to `x: &mut T`,
|
||||
/// `ref x` to `ref mut x`, etc.
|
||||
Type {
|
||||
span: Span,
|
||||
suggestion: String,
|
||||
additional: Option<(Span, String)>,
|
||||
},
|
||||
/// Suggestion for expressions, `&x` to `&mut x`, `&x[i]` to `&mut x[i]`, etc.
|
||||
Expr {
|
||||
span: Span,
|
||||
suggestion: String,
|
||||
},
|
||||
/// Suggests `.get_mut` in the case of `&map[&key]` for Hash/BTreeMap.
|
||||
MapGetMut {
|
||||
span: Span,
|
||||
suggestion: String,
|
||||
},
|
||||
ChangeBinding,
|
||||
}
|
||||
|
||||
// When we want to suggest a user change a local variable to be a `&mut`, there
|
||||
|
|
@ -1487,110 +1529,111 @@ struct AmpMutSugg {
|
|||
// This implementation attempts to emulate AST-borrowck prioritization
|
||||
// by trying (3.), then (2.) and finally falling back on (1.).
|
||||
fn suggest_ampmut<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
decl_ty: Ty<'tcx>,
|
||||
decl_span: Span,
|
||||
opt_assignment_rhs_span: Option<Span>,
|
||||
opt_ty_info: Option<Span>,
|
||||
infcx: &crate::BorrowckInferCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
opt_assignment_rhs_stmt: Option<&Statement<'tcx>>,
|
||||
) -> Option<AmpMutSugg> {
|
||||
// if there is a RHS and it starts with a `&` from it, then check if it is
|
||||
let tcx = infcx.tcx;
|
||||
// If there is a RHS and it starts with a `&` from it, then check if it is
|
||||
// mutable, and if not, put suggest putting `mut ` to make it mutable.
|
||||
// we don't have to worry about lifetime annotations here because they are
|
||||
// We don't have to worry about lifetime annotations here because they are
|
||||
// not valid when taking a reference. For example, the following is not valid Rust:
|
||||
//
|
||||
// let x: &i32 = &'a 5;
|
||||
// ^^ lifetime annotation not allowed
|
||||
//
|
||||
if let Some(rhs_span) = opt_assignment_rhs_span
|
||||
&& let Ok(rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
|
||||
&& let Some(rhs_str_no_amp) = rhs_str.strip_prefix('&')
|
||||
if let Some(rhs_stmt) = opt_assignment_rhs_stmt
|
||||
&& let StatementKind::Assign(box (lhs, rvalue)) = &rhs_stmt.kind
|
||||
&& let mut rhs_span = rhs_stmt.source_info.span
|
||||
&& let Ok(mut rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
|
||||
{
|
||||
// Suggest changing `&raw const` to `&raw mut` if applicable.
|
||||
if rhs_str_no_amp.trim_start().strip_prefix("raw const").is_some() {
|
||||
let const_idx = rhs_str.find("const").unwrap() as u32;
|
||||
let const_span = rhs_span
|
||||
.with_lo(rhs_span.lo() + BytePos(const_idx))
|
||||
.with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32));
|
||||
let mut rvalue = rvalue;
|
||||
|
||||
return Some(AmpMutSugg {
|
||||
has_sugg: true,
|
||||
span: const_span,
|
||||
suggestion: "mut".to_owned(),
|
||||
additional: None,
|
||||
});
|
||||
}
|
||||
|
||||
// Figure out if rhs already is `&mut`.
|
||||
let is_mut = if let Some(rest) = rhs_str_no_amp.trim_start().strip_prefix("mut") {
|
||||
match rest.chars().next() {
|
||||
// e.g. `&mut x`
|
||||
Some(c) if c.is_whitespace() => true,
|
||||
// e.g. `&mut(x)`
|
||||
Some('(') => true,
|
||||
// e.g. `&mut{x}`
|
||||
Some('{') => true,
|
||||
// e.g. `&mutablevar`
|
||||
_ => false,
|
||||
// Take some special care when handling `let _x = &*_y`:
|
||||
// We want to know if this is part of an overloaded index, so `let x = &a[0]`,
|
||||
// or whether this is a usertype ascription (`let _x: &T = y`).
|
||||
if let Rvalue::Ref(_, BorrowKind::Shared, place) = rvalue
|
||||
&& place.projection.len() == 1
|
||||
&& place.projection[0] == ProjectionElem::Deref
|
||||
&& let Some(assign) = find_assignments(&body, place.local).first()
|
||||
{
|
||||
// If this is a usertype ascription (`let _x: &T = _y`) then pierce through it as either we want
|
||||
// to suggest `&mut` on the expression (handled here) or we return `None` and let the caller
|
||||
// suggest `&mut` on the type if the expression seems fine (e.g. `let _x: &T = &mut _y`).
|
||||
if let Some(user_ty_projs) = body.local_decls[lhs.local].user_ty.as_ref()
|
||||
&& let [user_ty_proj] = user_ty_projs.contents.as_slice()
|
||||
&& user_ty_proj.projs.is_empty()
|
||||
&& let Either::Left(rhs_stmt_new) = body.stmt_at(*assign)
|
||||
&& let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind
|
||||
&& let rhs_span_new = rhs_stmt_new.source_info.span
|
||||
&& let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span)
|
||||
{
|
||||
(rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new);
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
// if the reference is already mutable then there is nothing we can do
|
||||
// here.
|
||||
if !is_mut {
|
||||
// shrink the span to just after the `&` in `&variable`
|
||||
let span = rhs_span.with_lo(rhs_span.lo() + BytePos(1)).shrink_to_lo();
|
||||
|
||||
// FIXME(Ezrashaw): returning is bad because we still might want to
|
||||
// update the annotated type, see #106857.
|
||||
return Some(AmpMutSugg {
|
||||
has_sugg: true,
|
||||
span,
|
||||
suggestion: "mut ".to_owned(),
|
||||
additional: None,
|
||||
});
|
||||
if let Either::Right(call) = body.stmt_at(*assign)
|
||||
&& let TerminatorKind::Call {
|
||||
func: Operand::Constant(box const_operand), args, ..
|
||||
} = &call.kind
|
||||
&& let ty::FnDef(method_def_id, method_args) = *const_operand.ty().kind()
|
||||
&& let Some(trait_) = tcx.trait_of_assoc(method_def_id)
|
||||
&& tcx.is_lang_item(trait_, hir::LangItem::Index)
|
||||
{
|
||||
let trait_ref = ty::TraitRef::from_assoc(
|
||||
tcx,
|
||||
tcx.require_lang_item(hir::LangItem::IndexMut, rhs_span),
|
||||
method_args,
|
||||
);
|
||||
// The type only implements `Index` but not `IndexMut`, we must not suggest `&mut`.
|
||||
if !infcx
|
||||
.type_implements_trait(trait_ref.def_id, trait_ref.args, infcx.param_env)
|
||||
.must_apply_considering_regions()
|
||||
{
|
||||
// Suggest `get_mut` if type is a `BTreeMap` or `HashMap`.
|
||||
if let ty::Adt(def, _) = trait_ref.self_ty().kind()
|
||||
&& [sym::BTreeMap, sym::HashMap]
|
||||
.into_iter()
|
||||
.any(|s| tcx.is_diagnostic_item(s, def.did()))
|
||||
&& let [map, key] = &**args
|
||||
&& let Ok(map) = tcx.sess.source_map().span_to_snippet(map.span)
|
||||
&& let Ok(key) = tcx.sess.source_map().span_to_snippet(key.span)
|
||||
{
|
||||
let span = rhs_span;
|
||||
let suggestion = format!("{map}.get_mut({key}).unwrap()");
|
||||
return Some(AmpMutSugg::MapGetMut { span, suggestion });
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let sugg = match rvalue {
|
||||
Rvalue::Ref(_, BorrowKind::Shared, _) if let Some(ref_idx) = rhs_str.find('&') => {
|
||||
// Shrink the span to just after the `&` in `&variable`.
|
||||
Some((
|
||||
rhs_span.with_lo(rhs_span.lo() + BytePos(ref_idx as u32 + 1)).shrink_to_lo(),
|
||||
"mut ".to_owned(),
|
||||
))
|
||||
}
|
||||
Rvalue::RawPtr(RawPtrKind::Const, _) if let Some(const_idx) = rhs_str.find("const") => {
|
||||
// Suggest changing `&raw const` to `&raw mut` if applicable.
|
||||
let const_idx = const_idx as u32;
|
||||
Some((
|
||||
rhs_span
|
||||
.with_lo(rhs_span.lo() + BytePos(const_idx))
|
||||
.with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)),
|
||||
"mut".to_owned(),
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some((span, suggestion)) = sugg {
|
||||
return Some(AmpMutSugg::Expr { span, suggestion });
|
||||
}
|
||||
}
|
||||
|
||||
let (binding_exists, span) = match opt_ty_info {
|
||||
// if this is a variable binding with an explicit type,
|
||||
// then we will suggest changing it to be mutable.
|
||||
// this is `Applicability::MachineApplicable`.
|
||||
Some(ty_span) => (true, ty_span),
|
||||
|
||||
// otherwise, we'll suggest *adding* an annotated type, we'll suggest
|
||||
// the RHS's type for that.
|
||||
// this is `Applicability::HasPlaceholders`.
|
||||
None => (false, decl_span),
|
||||
};
|
||||
|
||||
// if the binding already exists and is a reference with an explicit
|
||||
// lifetime, then we can suggest adding ` mut`. this is special-cased from
|
||||
// the path without an explicit lifetime.
|
||||
if let Ok(src) = tcx.sess.source_map().span_to_snippet(span)
|
||||
&& src.starts_with("&'")
|
||||
// note that `& 'a T` is invalid so this is correct.
|
||||
&& let Some(ws_pos) = src.find(char::is_whitespace)
|
||||
{
|
||||
let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
|
||||
Some(AmpMutSugg { has_sugg: true, span, suggestion: " mut".to_owned(), additional: None })
|
||||
// if there is already a binding, we modify it to be `mut`
|
||||
} else if binding_exists {
|
||||
// shrink the span to just after the `&` in `&variable`
|
||||
let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
|
||||
Some(AmpMutSugg { has_sugg: true, span, suggestion: "mut ".to_owned(), additional: None })
|
||||
} else {
|
||||
// otherwise, suggest that the user annotates the binding; we provide the
|
||||
// type of the local.
|
||||
let ty = decl_ty.builtin_deref(true).unwrap();
|
||||
|
||||
Some(AmpMutSugg {
|
||||
has_sugg: false,
|
||||
span,
|
||||
suggestion: format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty),
|
||||
additional: None,
|
||||
})
|
||||
}
|
||||
Some(AmpMutSugg::ChangeBinding)
|
||||
}
|
||||
|
||||
/// If the type is a `Coroutine`, `Closure`, or `CoroutineClosure`
|
||||
|
|
|
|||
|
|
@ -215,7 +215,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
diag: &mut Diag<'_>,
|
||||
lower_bound: RegionVid,
|
||||
) {
|
||||
let mut suggestions = vec![];
|
||||
let tcx = self.infcx.tcx;
|
||||
|
||||
// find generic associated types in the given region 'lower_bound'
|
||||
|
|
@ -237,9 +236,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
.collect::<Vec<_>>();
|
||||
debug!(?gat_id_and_generics);
|
||||
|
||||
// find higher-ranked trait bounds bounded to the generic associated types
|
||||
// Look for the where-bound which introduces the placeholder.
|
||||
// As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>`
|
||||
// and `T: for<'a> Trait`<'a>.
|
||||
let mut hrtb_bounds = vec![];
|
||||
gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| {
|
||||
gat_id_and_generics.iter().flatten().for_each(|&(gat_hir_id, generics)| {
|
||||
for pred in generics.predicates {
|
||||
let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) =
|
||||
pred.kind
|
||||
|
|
@ -248,17 +249,32 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
};
|
||||
if bound_generic_params
|
||||
.iter()
|
||||
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
|
||||
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
|
||||
.is_some()
|
||||
{
|
||||
for bound in *bounds {
|
||||
hrtb_bounds.push(bound);
|
||||
}
|
||||
} else {
|
||||
for bound in *bounds {
|
||||
if let Trait(trait_bound) = bound {
|
||||
if trait_bound
|
||||
.bound_generic_params
|
||||
.iter()
|
||||
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
|
||||
.is_some()
|
||||
{
|
||||
hrtb_bounds.push(bound);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
debug!(?hrtb_bounds);
|
||||
|
||||
let mut suggestions = vec![];
|
||||
hrtb_bounds.iter().for_each(|bound| {
|
||||
let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -124,8 +124,13 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
|
|||
// by using `ty_vid rel B` and then finally and end by equating `ty_vid` to
|
||||
// the opaque.
|
||||
let mut enable_subtyping = |ty, opaque_is_expected| {
|
||||
let ty_vid = infcx.next_ty_var_id_in_universe(self.span(), ty::UniverseIndex::ROOT);
|
||||
|
||||
// We create the fresh inference variable in the highest universe.
|
||||
// In theory we could limit it to the highest universe in the args of
|
||||
// the opaque but that isn't really worth the effort.
|
||||
//
|
||||
// We'll make sure that the opaque type can actually name everything
|
||||
// in its hidden type later on.
|
||||
let ty_vid = infcx.next_ty_vid(self.span());
|
||||
let variance = if opaque_is_expected {
|
||||
self.ambient_variance
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -27,21 +27,39 @@ pub(crate) fn expand_deriving_from(
|
|||
cx.dcx().bug("derive(From) used on something else than an item");
|
||||
};
|
||||
|
||||
// #[derive(From)] is currently usable only on structs with exactly one field.
|
||||
let field = if let ItemKind::Struct(_, _, data) = &item.kind
|
||||
&& let [field] = data.fields()
|
||||
{
|
||||
Some(field.clone())
|
||||
} else {
|
||||
None
|
||||
let err_span = || {
|
||||
let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span);
|
||||
MultiSpan::from_spans(vec![span, item_span])
|
||||
};
|
||||
|
||||
let from_type = match &field {
|
||||
Some(field) => Ty::AstTy(field.ty.clone()),
|
||||
// We don't have a type to put into From<...> if we don't have a single field, so just put
|
||||
// unit there.
|
||||
None => Ty::Unit,
|
||||
// `#[derive(From)]` is currently usable only on structs with exactly one field.
|
||||
let field = match &item.kind {
|
||||
ItemKind::Struct(_, _, data) => {
|
||||
if let [field] = data.fields() {
|
||||
Ok(field.clone())
|
||||
} else {
|
||||
let guar = cx.dcx().emit_err(errors::DeriveFromWrongFieldCount {
|
||||
span: err_span(),
|
||||
multiple_fields: data.fields().len() > 1,
|
||||
});
|
||||
Err(guar)
|
||||
}
|
||||
}
|
||||
ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => {
|
||||
let guar = cx.dcx().emit_err(errors::DeriveFromWrongTarget {
|
||||
span: err_span(),
|
||||
kind: &format!("{} {}", item.kind.article(), item.kind.descr()),
|
||||
});
|
||||
Err(guar)
|
||||
}
|
||||
_ => cx.dcx().bug("Invalid derive(From) ADT input"),
|
||||
};
|
||||
|
||||
let from_type = Ty::AstTy(match field {
|
||||
Ok(ref field) => field.ty.clone(),
|
||||
Err(guar) => cx.ty(span, ast::TyKind::Err(guar)),
|
||||
});
|
||||
|
||||
let path =
|
||||
Path::new_(pathvec_std!(convert::From), vec![Box::new(from_type.clone())], PathKind::Std);
|
||||
|
||||
|
|
@ -71,34 +89,17 @@ pub(crate) fn expand_deriving_from(
|
|||
attributes: thin_vec![cx.attr_word(sym::inline, span)],
|
||||
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
|
||||
combine_substructure: combine_substructure(Box::new(|cx, span, substructure| {
|
||||
let Some(field) = &field else {
|
||||
let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span);
|
||||
let err_span = MultiSpan::from_spans(vec![span, item_span]);
|
||||
let error = match &item.kind {
|
||||
ItemKind::Struct(_, _, data) => {
|
||||
cx.dcx().emit_err(errors::DeriveFromWrongFieldCount {
|
||||
span: err_span,
|
||||
multiple_fields: data.fields().len() > 1,
|
||||
})
|
||||
}
|
||||
ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => {
|
||||
cx.dcx().emit_err(errors::DeriveFromWrongTarget {
|
||||
span: err_span,
|
||||
kind: &format!("{} {}", item.kind.article(), item.kind.descr()),
|
||||
})
|
||||
}
|
||||
_ => cx.dcx().bug("Invalid derive(From) ADT input"),
|
||||
};
|
||||
|
||||
return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(error)));
|
||||
let field = match field {
|
||||
Ok(ref field) => field,
|
||||
Err(guar) => {
|
||||
return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(guar)));
|
||||
}
|
||||
};
|
||||
|
||||
let self_kw = Ident::new(kw::SelfUpper, span);
|
||||
let expr: Box<ast::Expr> = match substructure.fields {
|
||||
SubstructureFields::StaticStruct(variant, _) => match variant {
|
||||
// Self {
|
||||
// field: value
|
||||
// }
|
||||
// Self { field: value }
|
||||
VariantData::Struct { .. } => cx.expr_struct_ident(
|
||||
span,
|
||||
self_kw,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(autodiff)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(if_let_guard)]
|
||||
|
|
|
|||
|
|
@ -497,7 +497,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
|
|||
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-module", module));
|
||||
|
||||
let name =
|
||||
codegen_fn_attrs.link_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id()));
|
||||
codegen_fn_attrs.symbol_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id()));
|
||||
let name = name.as_str();
|
||||
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-name", name));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,104 +1,21 @@
|
|||
//! A helper class for dealing with static archives
|
||||
|
||||
use std::ffi::{CStr, CString, c_char, c_void};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{io, mem, ptr, str};
|
||||
use std::ffi::{CStr, c_char, c_void};
|
||||
use std::io;
|
||||
|
||||
use rustc_codegen_ssa::back::archive::{
|
||||
ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder,
|
||||
DEFAULT_OBJECT_READER, ObjectReader, UnknownArchiveKind, try_extract_macho_fat_archive,
|
||||
ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, ObjectReader,
|
||||
};
|
||||
use rustc_session::Session;
|
||||
|
||||
use crate::llvm::archive_ro::{ArchiveRO, Child};
|
||||
use crate::llvm::{self, ArchiveKind, last_error};
|
||||
|
||||
/// Helper for adding many files to an archive.
|
||||
#[must_use = "must call build() to finish building the archive"]
|
||||
pub(crate) struct LlvmArchiveBuilder<'a> {
|
||||
sess: &'a Session,
|
||||
additions: Vec<Addition>,
|
||||
}
|
||||
|
||||
enum Addition {
|
||||
File { path: PathBuf, name_in_archive: String },
|
||||
Archive { path: PathBuf, archive: ArchiveRO, skip: Box<dyn FnMut(&str) -> bool> },
|
||||
}
|
||||
|
||||
impl Addition {
|
||||
fn path(&self) -> &Path {
|
||||
match self {
|
||||
Addition::File { path, .. } | Addition::Archive { path, .. } => path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_relevant_child(c: &Child<'_>) -> bool {
|
||||
match c.name() {
|
||||
Some(name) => !name.contains("SYMDEF"),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ArchiveBuilder for LlvmArchiveBuilder<'a> {
|
||||
fn add_archive(
|
||||
&mut self,
|
||||
archive: &Path,
|
||||
skip: Box<dyn FnMut(&str) -> bool + 'static>,
|
||||
) -> io::Result<()> {
|
||||
let mut archive = archive.to_path_buf();
|
||||
if self.sess.target.llvm_target.contains("-apple-macosx") {
|
||||
if let Some(new_archive) = try_extract_macho_fat_archive(self.sess, &archive)? {
|
||||
archive = new_archive
|
||||
}
|
||||
}
|
||||
let archive_ro = match ArchiveRO::open(&archive) {
|
||||
Ok(ar) => ar,
|
||||
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
|
||||
};
|
||||
if self.additions.iter().any(|ar| ar.path() == archive) {
|
||||
return Ok(());
|
||||
}
|
||||
self.additions.push(Addition::Archive {
|
||||
path: archive,
|
||||
archive: archive_ro,
|
||||
skip: Box::new(skip),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds an arbitrary file to this archive
|
||||
fn add_file(&mut self, file: &Path) {
|
||||
let name = file.file_name().unwrap().to_str().unwrap();
|
||||
self.additions
|
||||
.push(Addition::File { path: file.to_path_buf(), name_in_archive: name.to_owned() });
|
||||
}
|
||||
|
||||
/// Combine the provided files, rlibs, and native libraries into a single
|
||||
/// `Archive`.
|
||||
fn build(mut self: Box<Self>, output: &Path) -> bool {
|
||||
match self.build_with_llvm(output) {
|
||||
Ok(any_members) => any_members,
|
||||
Err(error) => {
|
||||
self.sess.dcx().emit_fatal(ArchiveBuildFailure { path: output.to_owned(), error })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::llvm;
|
||||
|
||||
pub(crate) struct LlvmArchiveBuilderBuilder;
|
||||
|
||||
impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
|
||||
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> {
|
||||
// Keeping LlvmArchiveBuilder around in case of a regression caused by using
|
||||
// ArArchiveBuilder.
|
||||
// FIXME(#128955) remove a couple of months after #128936 gets merged in case
|
||||
// no regression is found.
|
||||
if false {
|
||||
Box::new(LlvmArchiveBuilder { sess, additions: Vec::new() })
|
||||
} else {
|
||||
Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER))
|
||||
}
|
||||
// Use the `object` crate to build archives, with a little bit of help from LLVM.
|
||||
Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -178,91 +95,3 @@ fn llvm_is_64_bit_object_file(buf: &[u8]) -> bool {
|
|||
fn llvm_is_ec_object_file(buf: &[u8]) -> bool {
|
||||
unsafe { llvm::LLVMRustIsECObject(buf.as_ptr(), buf.len()) }
|
||||
}
|
||||
|
||||
impl<'a> LlvmArchiveBuilder<'a> {
|
||||
fn build_with_llvm(&mut self, output: &Path) -> io::Result<bool> {
|
||||
let kind = &*self.sess.target.archive_format;
|
||||
let kind = kind
|
||||
.parse::<ArchiveKind>()
|
||||
.map_err(|_| kind)
|
||||
.unwrap_or_else(|kind| self.sess.dcx().emit_fatal(UnknownArchiveKind { kind }));
|
||||
|
||||
let mut additions = mem::take(&mut self.additions);
|
||||
// Values in the `members` list below will contain pointers to the strings allocated here.
|
||||
// So they need to get dropped after all elements of `members` get freed.
|
||||
let mut strings = Vec::new();
|
||||
let mut members = Vec::new();
|
||||
|
||||
let dst = CString::new(output.to_str().unwrap())?;
|
||||
|
||||
unsafe {
|
||||
for addition in &mut additions {
|
||||
match addition {
|
||||
Addition::File { path, name_in_archive } => {
|
||||
let path = CString::new(path.to_str().unwrap())?;
|
||||
let name = CString::new(name_in_archive.as_bytes())?;
|
||||
members.push(llvm::LLVMRustArchiveMemberNew(
|
||||
path.as_ptr(),
|
||||
name.as_ptr(),
|
||||
None,
|
||||
));
|
||||
strings.push(path);
|
||||
strings.push(name);
|
||||
}
|
||||
Addition::Archive { archive, skip, .. } => {
|
||||
for child in archive.iter() {
|
||||
let child = child.map_err(string_to_io_error)?;
|
||||
if !is_relevant_child(&child) {
|
||||
continue;
|
||||
}
|
||||
let child_name = child.name().unwrap();
|
||||
if skip(child_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// It appears that LLVM's archive writer is a little
|
||||
// buggy if the name we pass down isn't just the
|
||||
// filename component, so chop that off here and
|
||||
// pass it in.
|
||||
//
|
||||
// See LLVM bug 25877 for more info.
|
||||
let child_name =
|
||||
Path::new(child_name).file_name().unwrap().to_str().unwrap();
|
||||
let name = CString::new(child_name)?;
|
||||
let m = llvm::LLVMRustArchiveMemberNew(
|
||||
ptr::null(),
|
||||
name.as_ptr(),
|
||||
Some(child.raw),
|
||||
);
|
||||
members.push(m);
|
||||
strings.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let r = llvm::LLVMRustWriteArchive(
|
||||
dst.as_ptr(),
|
||||
members.len() as libc::size_t,
|
||||
members.as_ptr() as *const &_,
|
||||
true,
|
||||
kind,
|
||||
self.sess.target.arch == "arm64ec",
|
||||
);
|
||||
let ret = if r.into_result().is_err() {
|
||||
let msg = last_error().unwrap_or_else(|| "failed to write archive".into());
|
||||
Err(io::Error::new(io::ErrorKind::Other, msg))
|
||||
} else {
|
||||
Ok(!members.is_empty())
|
||||
};
|
||||
for member in members {
|
||||
llvm::LLVMRustArchiveMemberFree(member);
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn string_to_io_error(s: String) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}"))
|
||||
}
|
||||
|
|
|
|||
5
compiler/rustc_codegen_llvm/src/back/mod.rs
Normal file
5
compiler/rustc_codegen_llvm/src/back/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub(crate) mod archive;
|
||||
pub(crate) mod lto;
|
||||
pub(crate) mod owned_target_machine;
|
||||
mod profiling;
|
||||
pub(crate) mod write;
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
use std::ffi::{CStr, c_char};
|
||||
use std::assert_matches::assert_matches;
|
||||
use std::ffi::CStr;
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
|
|
@ -41,11 +42,9 @@ impl OwnedTargetMachine {
|
|||
args_cstr_buff: &[u8],
|
||||
use_wasm_eh: bool,
|
||||
) -> Result<Self, LlvmError<'static>> {
|
||||
assert!(args_cstr_buff.len() > 0);
|
||||
assert!(
|
||||
*args_cstr_buff.last().unwrap() == 0,
|
||||
"The last character must be a null terminator."
|
||||
);
|
||||
// The argument list is passed as the concatenation of one or more C strings.
|
||||
// This implies that there must be a last byte, and it must be 0.
|
||||
assert_matches!(args_cstr_buff, [.., b'\0'], "the last byte must be a NUL terminator");
|
||||
|
||||
// SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data
|
||||
let tm_ptr = unsafe {
|
||||
|
|
@ -71,7 +70,7 @@ impl OwnedTargetMachine {
|
|||
output_obj_file.as_ptr(),
|
||||
debug_info_compression.as_ptr(),
|
||||
use_emulated_tls,
|
||||
args_cstr_buff.as_ptr() as *const c_char,
|
||||
args_cstr_buff.as_ptr(),
|
||||
args_cstr_buff.len(),
|
||||
use_wasm_eh,
|
||||
)
|
||||
|
|
@ -99,7 +98,7 @@ impl Drop for OwnedTargetMachine {
|
|||
// llvm::LLVMRustCreateTargetMachine OwnedTargetMachine is not copyable so there is no
|
||||
// double free or use after free.
|
||||
unsafe {
|
||||
llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_mut());
|
||||
llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1453,7 +1453,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
instance: Option<Instance<'tcx>>,
|
||||
) {
|
||||
let call = self.call(llty, fn_attrs, Some(fn_abi), llfn, args, funclet, instance);
|
||||
llvm::LLVMRustSetTailCallKind(call, llvm::TailCallKind::MustTail);
|
||||
llvm::LLVMSetTailCallKind(call, llvm::TailCallKind::MustTail);
|
||||
|
||||
match &fn_abi.ret.mode {
|
||||
PassMode::Ignore | PassMode::Indirect { .. } => self.ret_void(),
|
||||
|
|
|
|||
|
|
@ -377,6 +377,15 @@ pub(crate) unsafe fn create_module<'ll>(
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(regparm_count) = sess.opts.unstable_opts.regparm {
|
||||
llvm::add_module_flag_u32(
|
||||
llmod,
|
||||
llvm::ModuleFlagMergeBehavior::Error,
|
||||
"NumRegisterParameters",
|
||||
regparm_count,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection {
|
||||
if sess.target.arch == "aarch64" {
|
||||
llvm::add_module_flag_u32(
|
||||
|
|
@ -462,6 +471,15 @@ pub(crate) unsafe fn create_module<'ll>(
|
|||
}
|
||||
}
|
||||
|
||||
if sess.opts.unstable_opts.indirect_branch_cs_prefix {
|
||||
llvm::add_module_flag_u32(
|
||||
llmod,
|
||||
llvm::ModuleFlagMergeBehavior::Override,
|
||||
"indirect_branch_cs_prefix",
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
match (sess.opts.unstable_opts.small_data_threshold, sess.target.small_data_threshold_support())
|
||||
{
|
||||
// Set up the small-data optimization limit for architectures that use
|
||||
|
|
|
|||
|
|
@ -46,18 +46,11 @@ use rustc_session::Session;
|
|||
use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest};
|
||||
use rustc_span::Symbol;
|
||||
|
||||
mod back {
|
||||
pub(crate) mod archive;
|
||||
pub(crate) mod lto;
|
||||
pub(crate) mod owned_target_machine;
|
||||
mod profiling;
|
||||
pub(crate) mod write;
|
||||
}
|
||||
|
||||
mod abi;
|
||||
mod allocator;
|
||||
mod asm;
|
||||
mod attributes;
|
||||
mod back;
|
||||
mod base;
|
||||
mod builder;
|
||||
mod callee;
|
||||
|
|
|
|||
|
|
@ -1,94 +0,0 @@
|
|||
//! A wrapper around LLVM's archive (.a) code
|
||||
|
||||
use std::path::Path;
|
||||
use std::{slice, str};
|
||||
|
||||
use rustc_fs_util::path_to_c_string;
|
||||
|
||||
pub(crate) struct ArchiveRO {
|
||||
pub raw: &'static mut super::Archive,
|
||||
}
|
||||
|
||||
unsafe impl Send for ArchiveRO {}
|
||||
|
||||
pub(crate) struct Iter<'a> {
|
||||
raw: &'a mut super::ArchiveIterator<'a>,
|
||||
}
|
||||
|
||||
pub(crate) struct Child<'a> {
|
||||
pub raw: &'a mut super::ArchiveChild<'a>,
|
||||
}
|
||||
|
||||
impl ArchiveRO {
|
||||
/// Opens a static archive for read-only purposes. This is more optimized
|
||||
/// than the `open` method because it uses LLVM's internal `Archive` class
|
||||
/// rather than shelling out to `ar` for everything.
|
||||
///
|
||||
/// If this archive is used with a mutable method, then an error will be
|
||||
/// raised.
|
||||
pub(crate) fn open(dst: &Path) -> Result<ArchiveRO, String> {
|
||||
unsafe {
|
||||
let s = path_to_c_string(dst);
|
||||
let ar = super::LLVMRustOpenArchive(s.as_ptr()).ok_or_else(|| {
|
||||
super::last_error().unwrap_or_else(|| "failed to open archive".to_owned())
|
||||
})?;
|
||||
Ok(ArchiveRO { raw: ar })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn iter(&self) -> Iter<'_> {
|
||||
unsafe { Iter { raw: super::LLVMRustArchiveIteratorNew(self.raw) } }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ArchiveRO {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
super::LLVMRustDestroyArchive(&mut *(self.raw as *mut _));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = Result<Child<'a>, String>;
|
||||
|
||||
fn next(&mut self) -> Option<Result<Child<'a>, String>> {
|
||||
unsafe {
|
||||
match super::LLVMRustArchiveIteratorNext(self.raw) {
|
||||
Some(raw) => Some(Ok(Child { raw })),
|
||||
None => super::last_error().map(Err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for Iter<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
super::LLVMRustArchiveIteratorFree(&mut *(self.raw as *mut _));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Child<'a> {
|
||||
pub(crate) fn name(&self) -> Option<&'a str> {
|
||||
unsafe {
|
||||
let mut name_len = 0;
|
||||
let name_ptr = super::LLVMRustArchiveChildName(self.raw, &mut name_len);
|
||||
if name_ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
let name = slice::from_raw_parts(name_ptr as *const u8, name_len as usize);
|
||||
str::from_utf8(name).ok().map(|s| s.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for Child<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
super::LLVMRustArchiveChildFree(&mut *(self.raw as *mut _));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -97,6 +97,7 @@ pub(crate) enum ModuleFlagMergeBehavior {
|
|||
|
||||
// Consts for the LLVM CallConv type, pre-cast to usize.
|
||||
|
||||
/// Must match the layout of `LLVMTailCallKind`.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
#[repr(C)]
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -332,10 +333,15 @@ impl RealPredicate {
|
|||
}
|
||||
}
|
||||
|
||||
/// LLVMTypeKind
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
/// Must match the layout of `LLVMTypeKind`.
|
||||
///
|
||||
/// Use [`RawEnum<TypeKind>`] for values of `LLVMTypeKind` returned from LLVM,
|
||||
/// to avoid risk of UB if LLVM adds new enum values.
|
||||
///
|
||||
/// All of LLVM's variants should be declared here, even if no Rust-side code refers
|
||||
/// to them, because unknown variants will cause [`RawEnum::to_rust`] to panic.
|
||||
#[derive(Copy, Clone, PartialEq, Debug, TryFromU32)]
|
||||
#[repr(C)]
|
||||
#[expect(dead_code, reason = "Some variants are unused, but are kept to match LLVM-C")]
|
||||
pub(crate) enum TypeKind {
|
||||
Void = 0,
|
||||
Half = 1,
|
||||
|
|
@ -610,17 +616,6 @@ pub(crate) enum DiagnosticLevel {
|
|||
Remark,
|
||||
}
|
||||
|
||||
/// LLVMRustArchiveKind
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub(crate) enum ArchiveKind {
|
||||
K_GNU,
|
||||
K_BSD,
|
||||
K_DARWIN,
|
||||
K_COFF,
|
||||
K_AIXBIG,
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
// LLVMRustThinLTOData
|
||||
pub(crate) type ThinLTOData;
|
||||
|
|
@ -769,19 +764,12 @@ pub(crate) struct Builder<'a>(InvariantOpaque<'a>);
|
|||
pub(crate) struct PassManager<'a>(InvariantOpaque<'a>);
|
||||
unsafe extern "C" {
|
||||
pub type TargetMachine;
|
||||
pub(crate) type Archive;
|
||||
}
|
||||
#[repr(C)]
|
||||
pub(crate) struct ArchiveIterator<'a>(InvariantOpaque<'a>);
|
||||
#[repr(C)]
|
||||
pub(crate) struct ArchiveChild<'a>(InvariantOpaque<'a>);
|
||||
unsafe extern "C" {
|
||||
pub(crate) type Twine;
|
||||
pub(crate) type DiagnosticInfo;
|
||||
pub(crate) type SMDiagnostic;
|
||||
}
|
||||
#[repr(C)]
|
||||
pub(crate) struct RustArchiveMember<'a>(InvariantOpaque<'a>);
|
||||
/// Opaque pointee of `LLVMOperandBundleRef`.
|
||||
#[repr(C)]
|
||||
pub(crate) struct OperandBundle<'a>(InvariantOpaque<'a>);
|
||||
|
|
@ -1046,6 +1034,8 @@ unsafe extern "C" {
|
|||
CanThrow: llvm::Bool,
|
||||
) -> &'ll Value;
|
||||
|
||||
pub(crate) safe fn LLVMGetTypeKind(Ty: &Type) -> RawEnum<TypeKind>;
|
||||
|
||||
// Operations on integer types
|
||||
pub(crate) fn LLVMInt1TypeInContext(C: &Context) -> &Type;
|
||||
pub(crate) fn LLVMInt8TypeInContext(C: &Context) -> &Type;
|
||||
|
|
@ -1197,7 +1187,7 @@ unsafe extern "C" {
|
|||
pub(crate) safe fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool;
|
||||
pub(crate) safe fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool);
|
||||
pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool);
|
||||
pub(crate) safe fn LLVMRustSetTailCallKind(CallInst: &Value, Kind: TailCallKind);
|
||||
pub(crate) safe fn LLVMSetTailCallKind(CallInst: &Value, kind: TailCallKind);
|
||||
|
||||
// Operations on attributes
|
||||
pub(crate) fn LLVMCreateStringAttribute(
|
||||
|
|
@ -1841,9 +1831,6 @@ unsafe extern "C" {
|
|||
// Create and destroy contexts.
|
||||
pub(crate) fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context;
|
||||
|
||||
/// See llvm::LLVMTypeKind::getTypeID.
|
||||
pub(crate) fn LLVMRustGetTypeKind(Ty: &Type) -> TypeKind;
|
||||
|
||||
// Operations on all values
|
||||
pub(crate) fn LLVMRustGlobalAddMetadata<'a>(
|
||||
Val: &'a Value,
|
||||
|
|
@ -2438,7 +2425,7 @@ unsafe extern "C" {
|
|||
OutputObjFile: *const c_char,
|
||||
DebugInfoCompression: *const c_char,
|
||||
UseEmulatedTls: bool,
|
||||
ArgsCstrBuff: *const c_char,
|
||||
ArgsCstrBuff: *const c_uchar, // See "PTR_LEN_STR".
|
||||
ArgsCstrBuffLen: usize,
|
||||
UseWasmEH: bool,
|
||||
) -> *mut TargetMachine;
|
||||
|
|
@ -2505,19 +2492,6 @@ unsafe extern "C" {
|
|||
pub(crate) fn LLVMRustSetNormalizedTarget(M: &Module, triple: *const c_char);
|
||||
pub(crate) fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t);
|
||||
|
||||
pub(crate) fn LLVMRustOpenArchive(path: *const c_char) -> Option<&'static mut Archive>;
|
||||
pub(crate) fn LLVMRustArchiveIteratorNew(AR: &Archive) -> &mut ArchiveIterator<'_>;
|
||||
pub(crate) fn LLVMRustArchiveIteratorNext<'a>(
|
||||
AIR: &ArchiveIterator<'a>,
|
||||
) -> Option<&'a mut ArchiveChild<'a>>;
|
||||
pub(crate) fn LLVMRustArchiveChildName(
|
||||
ACR: &ArchiveChild<'_>,
|
||||
size: &mut size_t,
|
||||
) -> *const c_char;
|
||||
pub(crate) fn LLVMRustArchiveChildFree<'a>(ACR: &'a mut ArchiveChild<'a>);
|
||||
pub(crate) fn LLVMRustArchiveIteratorFree<'a>(AIR: &'a mut ArchiveIterator<'a>);
|
||||
pub(crate) fn LLVMRustDestroyArchive(AR: &'static mut Archive);
|
||||
|
||||
pub(crate) fn LLVMRustWriteTwineToString(T: &Twine, s: &RustString);
|
||||
|
||||
pub(crate) fn LLVMRustUnpackOptimizationDiagnostic<'a>(
|
||||
|
|
@ -2555,21 +2529,6 @@ unsafe extern "C" {
|
|||
num_ranges: &mut usize,
|
||||
) -> bool;
|
||||
|
||||
pub(crate) fn LLVMRustWriteArchive(
|
||||
Dst: *const c_char,
|
||||
NumMembers: size_t,
|
||||
Members: *const &RustArchiveMember<'_>,
|
||||
WriteSymbtab: bool,
|
||||
Kind: ArchiveKind,
|
||||
isEC: bool,
|
||||
) -> LLVMRustResult;
|
||||
pub(crate) fn LLVMRustArchiveMemberNew<'a>(
|
||||
Filename: *const c_char,
|
||||
Name: *const c_char,
|
||||
Child: Option<&ArchiveChild<'a>>,
|
||||
) -> &'a mut RustArchiveMember<'a>;
|
||||
pub(crate) fn LLVMRustArchiveMemberFree<'a>(Member: &'a mut RustArchiveMember<'a>);
|
||||
|
||||
pub(crate) fn LLVMRustSetDataLayoutFromTargetMachine<'a>(M: &'a Module, TM: &'a TargetMachine);
|
||||
|
||||
pub(crate) fn LLVMRustPositionBuilderPastAllocas<'a>(B: &Builder<'a>, Fn: &'a Value);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
use std::ffi::{CStr, CString};
|
||||
use std::num::NonZero;
|
||||
use std::ptr;
|
||||
use std::str::FromStr;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use libc::c_uint;
|
||||
|
|
@ -16,7 +15,6 @@ pub(crate) use self::MetadataType::*;
|
|||
pub(crate) use self::ffi::*;
|
||||
use crate::common::AsCCharPtr;
|
||||
|
||||
pub(crate) mod archive_ro;
|
||||
pub(crate) mod diagnostic;
|
||||
pub(crate) mod enzyme_ffi;
|
||||
mod ffi;
|
||||
|
|
@ -152,21 +150,6 @@ pub(crate) enum CodeGenOptSize {
|
|||
CodeGenOptSizeAggressive = 2,
|
||||
}
|
||||
|
||||
impl FromStr for ArchiveKind {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"gnu" => Ok(ArchiveKind::K_GNU),
|
||||
"bsd" => Ok(ArchiveKind::K_BSD),
|
||||
"darwin" => Ok(ArchiveKind::K_DARWIN),
|
||||
"coff" => Ok(ArchiveKind::K_COFF),
|
||||
"aix_big" => Ok(ArchiveKind::K_AIXBIG),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn SetInstructionCallConv(instr: &Value, cc: CallConv) {
|
||||
unsafe {
|
||||
LLVMSetInstructionCallConv(instr, cc as c_uint);
|
||||
|
|
|
|||
|
|
@ -277,6 +277,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
|
|||
{
|
||||
None
|
||||
}
|
||||
("loongarch32" | "loongarch64", "32s") if get_version().0 < 21 => None,
|
||||
// Filter out features that are not supported by the current LLVM version
|
||||
("riscv32" | "riscv64", "zacas") if get_version().0 < 20 => None,
|
||||
(
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ impl<'ll, CX: Borrow<SCx<'ll>>> BaseTypeCodegenMethods for GenericCx<'ll, CX> {
|
|||
}
|
||||
|
||||
fn type_kind(&self, ty: &'ll Type) -> TypeKind {
|
||||
unsafe { llvm::LLVMRustGetTypeKind(ty).to_generic() }
|
||||
llvm::LLVMGetTypeKind(ty).to_rust().to_generic()
|
||||
}
|
||||
|
||||
fn type_ptr(&self) -> &'ll Type {
|
||||
|
|
|
|||
|
|
@ -171,8 +171,8 @@ codegen_ssa_invalid_monomorphization_unsupported_symbol = invalid monomorphizati
|
|||
|
||||
codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size = invalid monomorphization of `{$name}` intrinsic: unsupported {$symbol} from `{$in_ty}` with element `{$in_elem}` of size `{$size}` to `{$ret_ty}`
|
||||
|
||||
codegen_ssa_invalid_no_sanitize = invalid argument for `no_sanitize`
|
||||
.note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`
|
||||
codegen_ssa_invalid_sanitize = invalid argument for `sanitize`
|
||||
.note = expected one of: `address`, `kernel_address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow_call_stack`, or `thread`
|
||||
|
||||
codegen_ssa_invalid_windows_subsystem = invalid windows subsystem `{$subsystem}`, only `windows` and `console` are allowed
|
||||
|
||||
|
|
|
|||
|
|
@ -2435,6 +2435,13 @@ fn linker_with_args(
|
|||
// Passed after compiler-generated options to support manual overriding when necessary.
|
||||
add_user_defined_link_args(cmd, sess);
|
||||
|
||||
// ------------ Builtin configurable linker scripts ------------
|
||||
// The user's link args should be able to overwrite symbols in the compiler's
|
||||
// linker script that were weakly defined (i.e. defined with `PROVIDE()`). For this
|
||||
// to work correctly, the user needs to be able to specify linker arguments like
|
||||
// `--defsym` and `--script` *before* any builtin linker scripts are evaluated.
|
||||
add_link_script(cmd, sess, tmpdir, crate_type);
|
||||
|
||||
// ------------ Object code and libraries, order-dependent ------------
|
||||
|
||||
// Post-link CRT objects.
|
||||
|
|
@ -2469,8 +2476,6 @@ fn add_order_independent_options(
|
|||
|
||||
let apple_sdk_root = add_apple_sdk(cmd, sess, flavor);
|
||||
|
||||
add_link_script(cmd, sess, tmpdir, crate_type);
|
||||
|
||||
if sess.target.os == "fuchsia"
|
||||
&& crate_type == CrateType::Executable
|
||||
&& !matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))
|
||||
|
|
|
|||
|
|
@ -858,7 +858,7 @@ pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>(
|
|||
instance: Instance<'tcx>,
|
||||
) -> bool {
|
||||
fn is_llvm_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
if let Some(name) = tcx.codegen_fn_attrs(def_id).link_name {
|
||||
if let Some(name) = tcx.codegen_fn_attrs(def_id).symbol_name {
|
||||
name.as_str().starts_with("llvm.")
|
||||
} else {
|
||||
false
|
||||
|
|
|
|||
|
|
@ -76,32 +76,6 @@ fn parse_instruction_set_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<Instr
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME(jdonszelmann): remove when no_sanitize becomes a parsed attr
|
||||
fn parse_no_sanitize_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<SanitizerSet> {
|
||||
let list = attr.meta_item_list()?;
|
||||
let mut sanitizer_set = SanitizerSet::empty();
|
||||
|
||||
for item in list.iter() {
|
||||
match item.name() {
|
||||
Some(sym::address) => {
|
||||
sanitizer_set |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
|
||||
}
|
||||
Some(sym::cfi) => sanitizer_set |= SanitizerSet::CFI,
|
||||
Some(sym::kcfi) => sanitizer_set |= SanitizerSet::KCFI,
|
||||
Some(sym::memory) => sanitizer_set |= SanitizerSet::MEMORY,
|
||||
Some(sym::memtag) => sanitizer_set |= SanitizerSet::MEMTAG,
|
||||
Some(sym::shadow_call_stack) => sanitizer_set |= SanitizerSet::SHADOWCALLSTACK,
|
||||
Some(sym::thread) => sanitizer_set |= SanitizerSet::THREAD,
|
||||
Some(sym::hwaddress) => sanitizer_set |= SanitizerSet::HWADDRESS,
|
||||
_ => {
|
||||
tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(sanitizer_set)
|
||||
}
|
||||
|
||||
// FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr
|
||||
fn parse_patchable_function_entry(
|
||||
tcx: TyCtxt<'_>,
|
||||
|
|
@ -160,7 +134,7 @@ fn parse_patchable_function_entry(
|
|||
#[derive(Default)]
|
||||
struct InterestingAttributeDiagnosticSpans {
|
||||
link_ordinal: Option<Span>,
|
||||
no_sanitize: Option<Span>,
|
||||
sanitize: Option<Span>,
|
||||
inline: Option<Span>,
|
||||
no_mangle: Option<Span>,
|
||||
}
|
||||
|
|
@ -181,7 +155,7 @@ fn process_builtin_attrs(
|
|||
match p {
|
||||
AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
|
||||
AttributeKind::ExportName { name, .. } => {
|
||||
codegen_fn_attrs.export_name = Some(*name)
|
||||
codegen_fn_attrs.symbol_name = Some(*name)
|
||||
}
|
||||
AttributeKind::Inline(inline, span) => {
|
||||
codegen_fn_attrs.inline = *inline;
|
||||
|
|
@ -189,7 +163,13 @@ fn process_builtin_attrs(
|
|||
}
|
||||
AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
|
||||
AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
|
||||
AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name),
|
||||
AttributeKind::LinkName { name, .. } => {
|
||||
// FIXME Remove check for foreign functions once #[link_name] on non-foreign
|
||||
// functions is a hard error
|
||||
if tcx.is_foreign_item(did) {
|
||||
codegen_fn_attrs.symbol_name = Some(*name);
|
||||
}
|
||||
}
|
||||
AttributeKind::LinkOrdinal { ordinal, span } => {
|
||||
codegen_fn_attrs.link_ordinal = Some(*ordinal);
|
||||
interesting_spans.link_ordinal = Some(*span);
|
||||
|
|
@ -329,11 +309,7 @@ fn process_builtin_attrs(
|
|||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
|
||||
}
|
||||
sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
|
||||
sym::no_sanitize => {
|
||||
interesting_spans.no_sanitize = Some(attr.span());
|
||||
codegen_fn_attrs.no_sanitize |=
|
||||
parse_no_sanitize_attr(tcx, attr).unwrap_or_default();
|
||||
}
|
||||
sym::sanitize => interesting_spans.sanitize = Some(attr.span()),
|
||||
sym::instruction_set => {
|
||||
codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr)
|
||||
}
|
||||
|
|
@ -357,6 +333,8 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
|
|||
codegen_fn_attrs.alignment =
|
||||
Ord::max(codegen_fn_attrs.alignment, tcx.sess.opts.unstable_opts.min_function_alignment);
|
||||
|
||||
// Compute the disabled sanitizers.
|
||||
codegen_fn_attrs.no_sanitize |= tcx.disabled_sanitizers_for(did);
|
||||
// On trait methods, inherit the `#[align]` of the trait's method prototype.
|
||||
codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did));
|
||||
|
||||
|
|
@ -409,7 +387,7 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
|
|||
// * `#[rustc_std_internal_symbol]` mangles the symbol name in a special way
|
||||
// both for exports and imports through foreign items. This is handled further,
|
||||
// during symbol mangling logic.
|
||||
} else if codegen_fn_attrs.link_name.is_some() {
|
||||
} else if codegen_fn_attrs.symbol_name.is_some() {
|
||||
// * This can be overridden with the `#[link_name]` attribute
|
||||
} else {
|
||||
// NOTE: there's one more exception that we cannot apply here. On wasm,
|
||||
|
|
@ -454,17 +432,17 @@ fn check_result(
|
|||
if !codegen_fn_attrs.no_sanitize.is_empty()
|
||||
&& codegen_fn_attrs.inline.always()
|
||||
&& let (Some(no_sanitize_span), Some(inline_span)) =
|
||||
(interesting_spans.no_sanitize, interesting_spans.inline)
|
||||
(interesting_spans.sanitize, interesting_spans.inline)
|
||||
{
|
||||
let hir_id = tcx.local_def_id_to_hir_id(did);
|
||||
tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| {
|
||||
lint.primary_message("`no_sanitize` will have no effect after inlining");
|
||||
lint.primary_message("setting `sanitize` off will have no effect after inlining");
|
||||
lint.span_note(inline_span, "inlining requested here");
|
||||
})
|
||||
}
|
||||
|
||||
// error when specifying link_name together with link_ordinal
|
||||
if let Some(_) = codegen_fn_attrs.link_name
|
||||
if let Some(_) = codegen_fn_attrs.symbol_name
|
||||
&& let Some(_) = codegen_fn_attrs.link_ordinal
|
||||
{
|
||||
let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
|
||||
|
|
@ -515,8 +493,7 @@ fn handle_lang_items(
|
|||
&& let Some(link_name) = lang_item.link_name()
|
||||
{
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
|
||||
codegen_fn_attrs.export_name = Some(link_name);
|
||||
codegen_fn_attrs.link_name = Some(link_name);
|
||||
codegen_fn_attrs.symbol_name = Some(link_name);
|
||||
}
|
||||
|
||||
// error when using no_mangle on a lang item item
|
||||
|
|
@ -582,6 +559,93 @@ fn opt_trait_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
|
|||
}
|
||||
}
|
||||
|
||||
/// For an attr that has the `sanitize` attribute, read the list of
|
||||
/// disabled sanitizers. `current_attr` holds the information about
|
||||
/// previously parsed attributes.
|
||||
fn parse_sanitize_attr(
|
||||
tcx: TyCtxt<'_>,
|
||||
attr: &Attribute,
|
||||
current_attr: SanitizerSet,
|
||||
) -> SanitizerSet {
|
||||
let mut result = current_attr;
|
||||
if let Some(list) = attr.meta_item_list() {
|
||||
for item in list.iter() {
|
||||
let MetaItemInner::MetaItem(set) = item else {
|
||||
tcx.dcx().emit_err(errors::InvalidSanitize { span: attr.span() });
|
||||
break;
|
||||
};
|
||||
let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
|
||||
match segments.as_slice() {
|
||||
// Similar to clang, sanitize(address = ..) and
|
||||
// sanitize(kernel_address = ..) control both ASan and KASan
|
||||
// Source: https://reviews.llvm.org/D44981.
|
||||
[sym::address] | [sym::kernel_address] if set.value_str() == Some(sym::off) => {
|
||||
result |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
|
||||
}
|
||||
[sym::address] | [sym::kernel_address] if set.value_str() == Some(sym::on) => {
|
||||
result &= !SanitizerSet::ADDRESS;
|
||||
result &= !SanitizerSet::KERNELADDRESS;
|
||||
}
|
||||
[sym::cfi] if set.value_str() == Some(sym::off) => result |= SanitizerSet::CFI,
|
||||
[sym::cfi] if set.value_str() == Some(sym::on) => result &= !SanitizerSet::CFI,
|
||||
[sym::kcfi] if set.value_str() == Some(sym::off) => result |= SanitizerSet::KCFI,
|
||||
[sym::kcfi] if set.value_str() == Some(sym::on) => result &= !SanitizerSet::KCFI,
|
||||
[sym::memory] if set.value_str() == Some(sym::off) => {
|
||||
result |= SanitizerSet::MEMORY
|
||||
}
|
||||
[sym::memory] if set.value_str() == Some(sym::on) => {
|
||||
result &= !SanitizerSet::MEMORY
|
||||
}
|
||||
[sym::memtag] if set.value_str() == Some(sym::off) => {
|
||||
result |= SanitizerSet::MEMTAG
|
||||
}
|
||||
[sym::memtag] if set.value_str() == Some(sym::on) => {
|
||||
result &= !SanitizerSet::MEMTAG
|
||||
}
|
||||
[sym::shadow_call_stack] if set.value_str() == Some(sym::off) => {
|
||||
result |= SanitizerSet::SHADOWCALLSTACK
|
||||
}
|
||||
[sym::shadow_call_stack] if set.value_str() == Some(sym::on) => {
|
||||
result &= !SanitizerSet::SHADOWCALLSTACK
|
||||
}
|
||||
[sym::thread] if set.value_str() == Some(sym::off) => {
|
||||
result |= SanitizerSet::THREAD
|
||||
}
|
||||
[sym::thread] if set.value_str() == Some(sym::on) => {
|
||||
result &= !SanitizerSet::THREAD
|
||||
}
|
||||
[sym::hwaddress] if set.value_str() == Some(sym::off) => {
|
||||
result |= SanitizerSet::HWADDRESS
|
||||
}
|
||||
[sym::hwaddress] if set.value_str() == Some(sym::on) => {
|
||||
result &= !SanitizerSet::HWADDRESS
|
||||
}
|
||||
_ => {
|
||||
tcx.dcx().emit_err(errors::InvalidSanitize { span: attr.span() });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet {
|
||||
// Backtrack to the crate root.
|
||||
let disabled = match tcx.opt_local_parent(did) {
|
||||
// Check the parent (recursively).
|
||||
Some(parent) => tcx.disabled_sanitizers_for(parent),
|
||||
// We reached the crate root without seeing an attribute, so
|
||||
// there is no sanitizers to exclude.
|
||||
None => SanitizerSet::empty(),
|
||||
};
|
||||
|
||||
// Check for a sanitize annotation directly on this def.
|
||||
if let Some(attr) = tcx.get_attr(did, sym::sanitize) {
|
||||
return parse_sanitize_attr(tcx, attr, disabled);
|
||||
}
|
||||
disabled
|
||||
}
|
||||
|
||||
/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
|
||||
/// applied to the method prototype.
|
||||
fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
|
|
@ -706,6 +770,11 @@ pub fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
|||
}
|
||||
|
||||
pub(crate) fn provide(providers: &mut Providers) {
|
||||
*providers =
|
||||
Providers { codegen_fn_attrs, should_inherit_track_caller, inherited_align, ..*providers };
|
||||
*providers = Providers {
|
||||
codegen_fn_attrs,
|
||||
should_inherit_track_caller,
|
||||
inherited_align,
|
||||
disabled_sanitizers_for,
|
||||
..*providers
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1121,9 +1121,9 @@ impl IntoDiagArg for ExpectedPointerMutability {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_invalid_no_sanitize)]
|
||||
#[diag(codegen_ssa_invalid_sanitize)]
|
||||
#[note]
|
||||
pub(crate) struct InvalidNoSanitize {
|
||||
pub(crate) struct InvalidSanitize {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ fn parse_rust_feature_flag<'a>(
|
|||
while let Some(new_feature) = new_features.pop() {
|
||||
if features.insert(new_feature) {
|
||||
if let Some(implied_features) = inverse_implied_features.get(&new_feature) {
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
new_features.extend(implied_features)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,8 +27,9 @@ use crate::{enter_trace_span, fluent_generated as fluent};
|
|||
pub enum FnArg<'tcx, Prov: Provenance = CtfeProvenance> {
|
||||
/// Pass a copy of the given operand.
|
||||
Copy(OpTy<'tcx, Prov>),
|
||||
/// Allow for the argument to be passed in-place: destroy the value originally stored at that place and
|
||||
/// make the place inaccessible for the duration of the function call.
|
||||
/// Allow for the argument to be passed in-place: destroy the value originally stored at that
|
||||
/// place and make the place inaccessible for the duration of the function call. This *must* be
|
||||
/// an in-memory place so that we can do the proper alias checks.
|
||||
InPlace(MPlaceTy<'tcx, Prov>),
|
||||
}
|
||||
|
||||
|
|
@ -379,6 +380,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
// *Before* pushing the new frame, determine whether the return destination is in memory.
|
||||
// Need to use `place_to_op` to be *sure* we get the mplace if there is one.
|
||||
let destination_mplace = self.place_to_op(destination)?.as_mplace_or_imm().left();
|
||||
|
||||
// Push the "raw" frame -- this leaves locals uninitialized.
|
||||
self.push_stack_frame_raw(instance, body, destination, cont)?;
|
||||
|
||||
// If an error is raised here, pop the frame again to get an accurate backtrace.
|
||||
|
|
@ -496,7 +502,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
|
||||
// Protect return place for in-place return value passing.
|
||||
// We only need to protect anything if this is actually an in-memory place.
|
||||
if let Left(mplace) = destination.as_mplace_or_local() {
|
||||
if let Some(mplace) = destination_mplace {
|
||||
M::protect_in_place_function_argument(self, &mplace)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -325,8 +325,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let _trace = enter_trace_span!(
|
||||
M,
|
||||
"instantiate_from_frame_and_normalize_erasing_regions",
|
||||
"{}",
|
||||
frame.instance
|
||||
%frame.instance
|
||||
);
|
||||
frame
|
||||
.instance
|
||||
|
|
@ -583,6 +582,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
span: Span,
|
||||
layout: Option<TyAndLayout<'tcx>>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
||||
let _trace = enter_trace_span!(M, const_eval::eval_mir_constant, ?val);
|
||||
let const_val = val.eval(*self.tcx, self.typing_env, span).map_err(|err| {
|
||||
if M::ALL_CONSTS_ARE_PRECHECKED {
|
||||
match err {
|
||||
|
|
|
|||
|
|
@ -175,6 +175,16 @@ impl<Prov: Provenance> Immediate<Prov> {
|
|||
}
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
pub fn has_provenance(&self) -> bool {
|
||||
match self {
|
||||
Immediate::Scalar(scalar) => matches!(scalar, Scalar::Ptr { .. }),
|
||||
Immediate::ScalarPair(s1, s2) => {
|
||||
matches!(s1, Scalar::Ptr { .. }) || matches!(s2, Scalar::Ptr { .. })
|
||||
}
|
||||
Immediate::Uninit => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ScalarPair needs a type to interpret, so we often have an immediate and a type together
|
||||
|
|
|
|||
|
|
@ -234,6 +234,12 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
|
|||
}
|
||||
|
||||
/// A place is either an mplace or some local.
|
||||
///
|
||||
/// Note that the return value can be different even for logically identical places!
|
||||
/// Specifically, if a local is stored in-memory, this may return `Local` or `MPlaceTy`
|
||||
/// depending on how the place was constructed. In other words, seeing `Local` here does *not*
|
||||
/// imply that this place does not point to memory. Every caller must therefore always handle
|
||||
/// both cases.
|
||||
#[inline(always)]
|
||||
pub fn as_mplace_or_local(
|
||||
&self,
|
||||
|
|
@ -759,6 +765,13 @@ where
|
|||
&mut self,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// If this is an efficiently represented local variable without provenance, skip the
|
||||
// `as_mplace_or_mutable_local` that would otherwise force this local into memory.
|
||||
if let Right(imm) = dest.to_op(self)?.as_mplace_or_imm() {
|
||||
if !imm.has_provenance() {
|
||||
return interp_ok(());
|
||||
}
|
||||
}
|
||||
match self.as_mplace_or_mutable_local(&dest.to_place())? {
|
||||
Right((local_val, _local_layout, local)) => {
|
||||
local_val.clear_provenance()?;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use super::{
|
|||
MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, from_known_layout,
|
||||
interp_ok, throw_ub, throw_unsup,
|
||||
};
|
||||
use crate::errors;
|
||||
use crate::{enter_trace_span, errors};
|
||||
|
||||
// The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread
|
||||
// boundary and dropped in the other thread, it would exit the span in the other thread.
|
||||
|
|
@ -386,6 +386,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
|
||||
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
|
||||
for &const_ in body.required_consts() {
|
||||
// We can't use `eval_mir_constant` here as that assumes that all required consts have
|
||||
// already been checked, so we need a separate tracing call.
|
||||
let _trace = enter_trace_span!(M, const_eval::required_consts, ?const_.const_);
|
||||
let c =
|
||||
self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
|
||||
c.eval(*self.tcx, self.typing_env, const_.span).map_err(|err| {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use either::Either;
|
||||
use rustc_abi::{FIRST_VARIANT, FieldIdx};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_index::IndexSlice;
|
||||
use rustc_middle::ty::{self, Instance, Ty};
|
||||
use rustc_middle::{bug, mir, span_bug};
|
||||
|
|
@ -389,8 +390,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
|
||||
/// Evaluate the arguments of a function call
|
||||
fn eval_fn_call_argument(
|
||||
&self,
|
||||
&mut self,
|
||||
op: &mir::Operand<'tcx>,
|
||||
move_definitely_disjoint: bool,
|
||||
) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> {
|
||||
interp_ok(match op {
|
||||
mir::Operand::Copy(_) | mir::Operand::Constant(_) => {
|
||||
|
|
@ -399,24 +401,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
FnArg::Copy(op)
|
||||
}
|
||||
mir::Operand::Move(place) => {
|
||||
// If this place lives in memory, preserve its location.
|
||||
// We call `place_to_op` which will be an `MPlaceTy` whenever there exists
|
||||
// an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local`
|
||||
// which can return a local even if that has an mplace.)
|
||||
let place = self.eval_place(*place)?;
|
||||
let op = self.place_to_op(&place)?;
|
||||
|
||||
match op.as_mplace_or_imm() {
|
||||
Either::Left(mplace) => FnArg::InPlace(mplace),
|
||||
Either::Right(_imm) => {
|
||||
// This argument doesn't live in memory, so there's no place
|
||||
// to make inaccessible during the call.
|
||||
// We rely on there not being any stray `PlaceTy` that would let the
|
||||
// caller directly access this local!
|
||||
// This is also crucial for tail calls, where we want the `FnArg` to
|
||||
// stay valid when the old stack frame gets popped.
|
||||
FnArg::Copy(op)
|
||||
if move_definitely_disjoint {
|
||||
// We still have to ensure that no *other* pointers are used to access this place,
|
||||
// so *if* it is in memory then we have to treat it as `InPlace`.
|
||||
// Use `place_to_op` to guarantee that we notice it being in memory.
|
||||
let op = self.place_to_op(&place)?;
|
||||
match op.as_mplace_or_imm() {
|
||||
Either::Left(mplace) => FnArg::InPlace(mplace),
|
||||
Either::Right(_imm) => FnArg::Copy(op),
|
||||
}
|
||||
} else {
|
||||
// We have to force this into memory to detect aliasing among `Move` arguments.
|
||||
FnArg::InPlace(self.force_allocation(&place)?)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -425,18 +422,46 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
/// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the
|
||||
/// necessary information about callee and arguments to make a call.
|
||||
fn eval_callee_and_args(
|
||||
&self,
|
||||
&mut self,
|
||||
terminator: &mir::Terminator<'tcx>,
|
||||
func: &mir::Operand<'tcx>,
|
||||
args: &[Spanned<mir::Operand<'tcx>>],
|
||||
) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> {
|
||||
let func = self.eval_operand(func, None)?;
|
||||
|
||||
// Evaluating function call arguments. The tricky part here is dealing with `Move`
|
||||
// arguments: we have to ensure no two such arguments alias. This would be most easily done
|
||||
// by just forcing them all into memory and then doing the usual in-place argument
|
||||
// protection, but then we'd force *a lot* of arguments into memory. So we do some syntactic
|
||||
// pre-processing here where if all `move` arguments are syntactically distinct local
|
||||
// variables (and none is indirect), we can skip the in-memory forcing.
|
||||
let move_definitely_disjoint = 'move_definitely_disjoint: {
|
||||
let mut previous_locals = FxHashSet::<mir::Local>::default();
|
||||
for arg in args {
|
||||
let mir::Operand::Move(place) = arg.node else {
|
||||
continue; // we can skip non-`Move` arguments.
|
||||
};
|
||||
if place.is_indirect_first_projection() {
|
||||
// An indirect `Move` argument could alias with anything else...
|
||||
break 'move_definitely_disjoint false;
|
||||
}
|
||||
if !previous_locals.insert(place.local) {
|
||||
// This local is the base for two arguments! They might overlap.
|
||||
break 'move_definitely_disjoint false;
|
||||
}
|
||||
}
|
||||
// We found no violation so they are all definitely disjoint.
|
||||
true
|
||||
};
|
||||
let args = args
|
||||
.iter()
|
||||
.map(|arg| self.eval_fn_call_argument(&arg.node))
|
||||
.map(|arg| self.eval_fn_call_argument(&arg.node, move_definitely_disjoint))
|
||||
.collect::<InterpResult<'tcx, Vec<_>>>()?;
|
||||
|
||||
let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx);
|
||||
let fn_sig_binder = {
|
||||
let _trace = enter_trace_span!(M, "fn_sig", ty = ?func.layout.ty.kind());
|
||||
func.layout.ty.fn_sig(*self.tcx)
|
||||
};
|
||||
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.typing_env, fn_sig_binder);
|
||||
let extra_args = &args[fn_sig.inputs().len()..];
|
||||
let extra_args =
|
||||
|
|
|
|||
|
|
@ -1418,7 +1418,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let _trace = enter_trace_span!(
|
||||
M,
|
||||
"validate_operand",
|
||||
"recursive={recursive}, reset_provenance_and_padding={reset_provenance_and_padding}, val={val:?}"
|
||||
recursive,
|
||||
reset_provenance_and_padding,
|
||||
?val,
|
||||
);
|
||||
|
||||
// Note that we *could* actually be in CTFE here with `-Zextra-const-ub-checks`, but it's
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use rustc_abi::TargetDataLayoutErrors;
|
|||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_hir::RustcVersion;
|
||||
use rustc_hir::attrs::{MirDialect, MirPhase};
|
||||
use rustc_macros::Subdiagnostic;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol};
|
||||
|
|
@ -312,6 +313,28 @@ impl IntoDiagArg for ExprPrecedence {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoDiagArg for MirDialect {
|
||||
fn into_diag_arg(self, _path: &mut Option<PathBuf>) -> DiagArgValue {
|
||||
let arg = match self {
|
||||
MirDialect::Analysis => "analysis",
|
||||
MirDialect::Built => "built",
|
||||
MirDialect::Runtime => "runtime",
|
||||
};
|
||||
DiagArgValue::Str(Cow::Borrowed(arg))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDiagArg for MirPhase {
|
||||
fn into_diag_arg(self, _path: &mut Option<PathBuf>) -> DiagArgValue {
|
||||
let arg = match self {
|
||||
MirPhase::Initial => "initial",
|
||||
MirPhase::PostCleanup => "post-cleanup",
|
||||
MirPhase::Optimized => "optimized",
|
||||
};
|
||||
DiagArgValue::Str(Cow::Borrowed(arg))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DiagSymbolList<S = Symbol>(Vec<S>);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use std::path::Path;
|
|||
use std::sync::Arc;
|
||||
|
||||
use derive_setters::Setters;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::sync::{DynSend, IntoDynSyncSend};
|
||||
use rustc_error_messages::{FluentArgs, SpanLabel};
|
||||
use rustc_lexer;
|
||||
|
|
@ -1853,7 +1853,7 @@ impl HumanEmitter {
|
|||
&& line_idx + 1 == annotated_file.lines.len(),
|
||||
);
|
||||
|
||||
let mut to_add = FxHashMap::default();
|
||||
let mut to_add = FxIndexMap::default();
|
||||
|
||||
for (depth, style) in depths {
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ expand_invalid_fragment_specifier =
|
|||
invalid fragment specifier `{$fragment}`
|
||||
.help = {$help}
|
||||
|
||||
expand_macro_args_bad_delim = macro attribute argument matchers require parentheses
|
||||
expand_macro_args_bad_delim = `{$rule_kw}` rule argument matchers require parentheses
|
||||
expand_macro_args_bad_delim_sugg = the delimiters should be `(` and `)`
|
||||
|
||||
expand_macro_body_stability =
|
||||
|
|
|
|||
|
|
@ -490,6 +490,7 @@ pub(crate) struct MacroArgsBadDelim {
|
|||
pub span: Span,
|
||||
#[subdiagnostic]
|
||||
pub sugg: MacroArgsBadDelimSugg,
|
||||
pub rule_kw: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use rustc_attr_parsing::{EvalConfigResult, ShouldEmit};
|
|||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||
use rustc_errors::PResult;
|
||||
use rustc_feature::Features;
|
||||
use rustc_hir::def::MacroKinds;
|
||||
use rustc_parse::parser::{
|
||||
AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma,
|
||||
token_descr,
|
||||
|
|
@ -565,6 +566,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
.map(|DeriveResolution { path, item, exts: _, is_const }| {
|
||||
// FIXME: Consider using the derive resolutions (`_exts`)
|
||||
// instead of enqueuing the derives to be resolved again later.
|
||||
// Note that this can result in duplicate diagnostics.
|
||||
let expn_id = LocalExpnId::fresh_empty();
|
||||
derive_invocations.push((
|
||||
Invocation {
|
||||
|
|
@ -922,6 +924,35 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
}
|
||||
fragment
|
||||
}
|
||||
SyntaxExtensionKind::MacroRules(expander)
|
||||
if expander.kinds().contains(MacroKinds::DERIVE) =>
|
||||
{
|
||||
if is_const {
|
||||
let guar = self
|
||||
.cx
|
||||
.dcx()
|
||||
.span_err(span, "macro `derive` does not support const derives");
|
||||
return ExpandResult::Ready(fragment_kind.dummy(span, guar));
|
||||
}
|
||||
let body = item.to_tokens();
|
||||
match expander.expand_derive(self.cx, span, &body) {
|
||||
Ok(tok_result) => {
|
||||
let fragment =
|
||||
self.parse_ast_fragment(tok_result, fragment_kind, &path, span);
|
||||
if macro_stats {
|
||||
update_derive_macro_stats(
|
||||
self.cx,
|
||||
fragment_kind,
|
||||
span,
|
||||
&path,
|
||||
&fragment,
|
||||
);
|
||||
}
|
||||
fragment
|
||||
}
|
||||
Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
InvocationKind::GlobDelegation { item, of_trait } => {
|
||||
|
|
|
|||
|
|
@ -14,14 +14,22 @@ use super::macro_rules::{MacroRule, NoopTracker, parser_from_cx};
|
|||
use crate::expand::{AstFragmentKind, parse_ast_fragment};
|
||||
use crate::mbe::macro_parser::ParseResult::*;
|
||||
use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser};
|
||||
use crate::mbe::macro_rules::{Tracker, try_match_macro, try_match_macro_attr};
|
||||
use crate::mbe::macro_rules::{
|
||||
Tracker, try_match_macro, try_match_macro_attr, try_match_macro_derive,
|
||||
};
|
||||
|
||||
pub(super) enum FailedMacro<'a> {
|
||||
Func,
|
||||
Attr(&'a TokenStream),
|
||||
Derive,
|
||||
}
|
||||
|
||||
pub(super) fn failed_to_match_macro(
|
||||
psess: &ParseSess,
|
||||
sp: Span,
|
||||
def_span: Span,
|
||||
name: Ident,
|
||||
attr_args: Option<&TokenStream>,
|
||||
args: FailedMacro<'_>,
|
||||
body: &TokenStream,
|
||||
rules: &[MacroRule],
|
||||
) -> (Span, ErrorGuaranteed) {
|
||||
|
|
@ -36,10 +44,12 @@ pub(super) fn failed_to_match_macro(
|
|||
// diagnostics.
|
||||
let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp);
|
||||
|
||||
let try_success_result = if let Some(attr_args) = attr_args {
|
||||
try_match_macro_attr(psess, name, attr_args, body, rules, &mut tracker)
|
||||
} else {
|
||||
try_match_macro(psess, name, body, rules, &mut tracker)
|
||||
let try_success_result = match args {
|
||||
FailedMacro::Func => try_match_macro(psess, name, body, rules, &mut tracker),
|
||||
FailedMacro::Attr(attr_args) => {
|
||||
try_match_macro_attr(psess, name, attr_args, body, rules, &mut tracker)
|
||||
}
|
||||
FailedMacro::Derive => try_match_macro_derive(psess, name, body, rules, &mut tracker),
|
||||
};
|
||||
|
||||
if try_success_result.is_ok() {
|
||||
|
|
@ -90,7 +100,7 @@ pub(super) fn failed_to_match_macro(
|
|||
}
|
||||
|
||||
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
|
||||
if attr_args.is_none()
|
||||
if let FailedMacro::Func = args
|
||||
&& let Some((body, comma_span)) = body.add_comma()
|
||||
{
|
||||
for rule in rules {
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ use rustc_session::Session;
|
|||
use rustc_session::parse::{ParseSess, feature_err};
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{Ident, Span, kw, sym};
|
||||
use rustc_span::{Ident, Span, Symbol, kw, sym};
|
||||
use tracing::{debug, instrument, trace, trace_span};
|
||||
|
||||
use super::diagnostics::failed_to_match_macro;
|
||||
use super::diagnostics::{FailedMacro, failed_to_match_macro};
|
||||
use super::macro_parser::{NamedMatches, NamedParseResult};
|
||||
use super::{SequenceRepetition, diagnostics};
|
||||
use crate::base::{
|
||||
|
|
@ -138,6 +138,8 @@ pub(super) enum MacroRule {
|
|||
body_span: Span,
|
||||
rhs: mbe::TokenTree,
|
||||
},
|
||||
/// A derive rule, for use with `#[m]`
|
||||
Derive { body: Vec<MatcherLoc>, body_span: Span, rhs: mbe::TokenTree },
|
||||
}
|
||||
|
||||
pub struct MacroRulesMacroExpander {
|
||||
|
|
@ -157,6 +159,7 @@ impl MacroRulesMacroExpander {
|
|||
MacroRule::Attr { args_span, body_span, ref rhs, .. } => {
|
||||
(MultiSpan::from_spans(vec![args_span, body_span]), rhs)
|
||||
}
|
||||
MacroRule::Derive { body_span, ref rhs, .. } => (MultiSpan::from_span(body_span), rhs),
|
||||
};
|
||||
if has_compile_error_macro(rhs) { None } else { Some((&self.name, span)) }
|
||||
}
|
||||
|
|
@ -164,6 +167,63 @@ impl MacroRulesMacroExpander {
|
|||
pub fn kinds(&self) -> MacroKinds {
|
||||
self.kinds
|
||||
}
|
||||
|
||||
pub fn expand_derive(
|
||||
&self,
|
||||
cx: &mut ExtCtxt<'_>,
|
||||
sp: Span,
|
||||
body: &TokenStream,
|
||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||
// This is similar to `expand_macro`, but they have very different signatures, and will
|
||||
// diverge further once derives support arguments.
|
||||
let Self { name, ref rules, node_id, .. } = *self;
|
||||
let psess = &cx.sess.psess;
|
||||
|
||||
if cx.trace_macros() {
|
||||
let msg = format!("expanding `#[derive({name})] {}`", pprust::tts_to_string(body));
|
||||
trace_macros_note(&mut cx.expansions, sp, msg);
|
||||
}
|
||||
|
||||
match try_match_macro_derive(psess, name, body, rules, &mut NoopTracker) {
|
||||
Ok((rule_index, rule, named_matches)) => {
|
||||
let MacroRule::Derive { rhs, .. } = rule else {
|
||||
panic!("try_match_macro_derive returned non-derive rule");
|
||||
};
|
||||
let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else {
|
||||
cx.dcx().span_bug(sp, "malformed macro derive rhs");
|
||||
};
|
||||
|
||||
let id = cx.current_expansion.id;
|
||||
let tts = transcribe(psess, &named_matches, rhs, *rhs_span, self.transparency, id)
|
||||
.map_err(|e| e.emit())?;
|
||||
|
||||
if cx.trace_macros() {
|
||||
let msg = format!("to `{}`", pprust::tts_to_string(&tts));
|
||||
trace_macros_note(&mut cx.expansions, sp, msg);
|
||||
}
|
||||
|
||||
if is_defined_in_current_crate(node_id) {
|
||||
cx.resolver.record_macro_rule_usage(node_id, rule_index);
|
||||
}
|
||||
|
||||
Ok(tts)
|
||||
}
|
||||
Err(CanRetry::No(guar)) => Err(guar),
|
||||
Err(CanRetry::Yes) => {
|
||||
let (_, guar) = failed_to_match_macro(
|
||||
cx.psess(),
|
||||
sp,
|
||||
self.span,
|
||||
name,
|
||||
FailedMacro::Derive,
|
||||
body,
|
||||
rules,
|
||||
);
|
||||
cx.macro_error_and_trace_macros_diag();
|
||||
Err(guar)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TTMacroExpander for MacroRulesMacroExpander {
|
||||
|
|
@ -325,8 +385,15 @@ fn expand_macro<'cx>(
|
|||
}
|
||||
Err(CanRetry::Yes) => {
|
||||
// Retry and emit a better error.
|
||||
let (span, guar) =
|
||||
failed_to_match_macro(cx.psess(), sp, def_span, name, None, &arg, rules);
|
||||
let (span, guar) = failed_to_match_macro(
|
||||
cx.psess(),
|
||||
sp,
|
||||
def_span,
|
||||
name,
|
||||
FailedMacro::Func,
|
||||
&arg,
|
||||
rules,
|
||||
);
|
||||
cx.macro_error_and_trace_macros_diag();
|
||||
DummyResult::any(span, guar)
|
||||
}
|
||||
|
|
@ -388,8 +455,15 @@ fn expand_macro_attr(
|
|||
Err(CanRetry::No(guar)) => Err(guar),
|
||||
Err(CanRetry::Yes) => {
|
||||
// Retry and emit a better error.
|
||||
let (_, guar) =
|
||||
failed_to_match_macro(cx.psess(), sp, def_span, name, Some(&args), &body, rules);
|
||||
let (_, guar) = failed_to_match_macro(
|
||||
cx.psess(),
|
||||
sp,
|
||||
def_span,
|
||||
name,
|
||||
FailedMacro::Attr(&args),
|
||||
&body,
|
||||
rules,
|
||||
);
|
||||
cx.trace_macros_diag();
|
||||
Err(guar)
|
||||
}
|
||||
|
|
@ -522,6 +596,7 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>(
|
|||
match result {
|
||||
Success(body_named_matches) => {
|
||||
psess.gated_spans.merge(gated_spans_snapshot);
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
named_matches.extend(body_named_matches);
|
||||
return Ok((i, rule, named_matches));
|
||||
}
|
||||
|
|
@ -536,6 +611,44 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>(
|
|||
Err(CanRetry::Yes)
|
||||
}
|
||||
|
||||
/// Try expanding the macro derive. Returns the index of the successful arm and its
|
||||
/// named_matches if it was successful, and nothing if it failed. On failure, it's the caller's job
|
||||
/// to use `track` accordingly to record all errors correctly.
|
||||
#[instrument(level = "debug", skip(psess, body, rules, track), fields(tracking = %T::description()))]
|
||||
pub(super) fn try_match_macro_derive<'matcher, T: Tracker<'matcher>>(
|
||||
psess: &ParseSess,
|
||||
name: Ident,
|
||||
body: &TokenStream,
|
||||
rules: &'matcher [MacroRule],
|
||||
track: &mut T,
|
||||
) -> Result<(usize, &'matcher MacroRule, NamedMatches), CanRetry> {
|
||||
// This uses the same strategy as `try_match_macro`
|
||||
let body_parser = parser_from_cx(psess, body.clone(), T::recovery());
|
||||
let mut tt_parser = TtParser::new(name);
|
||||
for (i, rule) in rules.iter().enumerate() {
|
||||
let MacroRule::Derive { body, .. } = rule else { continue };
|
||||
|
||||
let mut gated_spans_snapshot = mem::take(&mut *psess.gated_spans.spans.borrow_mut());
|
||||
|
||||
let result = tt_parser.parse_tt(&mut Cow::Borrowed(&body_parser), body, track);
|
||||
track.after_arm(true, &result);
|
||||
|
||||
match result {
|
||||
Success(named_matches) => {
|
||||
psess.gated_spans.merge(gated_spans_snapshot);
|
||||
return Ok((i, rule, named_matches));
|
||||
}
|
||||
Failure(_) => {
|
||||
mem::swap(&mut gated_spans_snapshot, &mut psess.gated_spans.spans.borrow_mut())
|
||||
}
|
||||
Error(_, _) => return Err(CanRetry::Yes),
|
||||
ErrorReported(guar) => return Err(CanRetry::No(guar)),
|
||||
}
|
||||
}
|
||||
|
||||
Err(CanRetry::Yes)
|
||||
}
|
||||
|
||||
/// Converts a macro item into a syntax extension.
|
||||
pub fn compile_declarative_macro(
|
||||
sess: &Session,
|
||||
|
|
@ -569,7 +682,7 @@ pub fn compile_declarative_macro(
|
|||
let mut rules = Vec::new();
|
||||
|
||||
while p.token != token::Eof {
|
||||
let args = if p.eat_keyword_noexpect(sym::attr) {
|
||||
let (args, is_derive) = if p.eat_keyword_noexpect(sym::attr) {
|
||||
kinds |= MacroKinds::ATTR;
|
||||
if !features.macro_attr() {
|
||||
feature_err(sess, sym::macro_attr, span, "`macro_rules!` attributes are unstable")
|
||||
|
|
@ -579,16 +692,46 @@ pub fn compile_declarative_macro(
|
|||
return dummy_syn_ext(guar);
|
||||
}
|
||||
let args = p.parse_token_tree();
|
||||
check_args_parens(sess, &args);
|
||||
check_args_parens(sess, sym::attr, &args);
|
||||
let args = parse_one_tt(args, RulePart::Pattern, sess, node_id, features, edition);
|
||||
check_emission(check_lhs(sess, node_id, &args));
|
||||
if let Some(guar) = check_no_eof(sess, &p, "expected macro attr body") {
|
||||
return dummy_syn_ext(guar);
|
||||
}
|
||||
Some(args)
|
||||
(Some(args), false)
|
||||
} else if p.eat_keyword_noexpect(sym::derive) {
|
||||
kinds |= MacroKinds::DERIVE;
|
||||
let derive_keyword_span = p.prev_token.span;
|
||||
if !features.macro_derive() {
|
||||
feature_err(sess, sym::macro_attr, span, "`macro_rules!` derives are unstable")
|
||||
.emit();
|
||||
}
|
||||
if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") {
|
||||
return dummy_syn_ext(guar);
|
||||
}
|
||||
let args = p.parse_token_tree();
|
||||
check_args_parens(sess, sym::derive, &args);
|
||||
let args_empty_result = check_args_empty(sess, &args);
|
||||
let args_not_empty = args_empty_result.is_err();
|
||||
check_emission(args_empty_result);
|
||||
if let Some(guar) = check_no_eof(sess, &p, "expected macro derive body") {
|
||||
return dummy_syn_ext(guar);
|
||||
}
|
||||
// If the user has `=>` right after the `()`, they might have forgotten the empty
|
||||
// parentheses.
|
||||
if p.token == token::FatArrow {
|
||||
let mut err = sess
|
||||
.dcx()
|
||||
.struct_span_err(p.token.span, "expected macro derive body, got `=>`");
|
||||
if args_not_empty {
|
||||
err.span_label(derive_keyword_span, "need `()` after this `derive`");
|
||||
}
|
||||
return dummy_syn_ext(err.emit());
|
||||
}
|
||||
(None, true)
|
||||
} else {
|
||||
kinds |= MacroKinds::BANG;
|
||||
None
|
||||
(None, false)
|
||||
};
|
||||
let lhs_tt = p.parse_token_tree();
|
||||
let lhs_tt = parse_one_tt(lhs_tt, RulePart::Pattern, sess, node_id, features, edition);
|
||||
|
|
@ -619,6 +762,8 @@ pub fn compile_declarative_macro(
|
|||
let args = mbe::macro_parser::compute_locs(&delimited.tts);
|
||||
let body_span = lhs_span;
|
||||
rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs: rhs_tt });
|
||||
} else if is_derive {
|
||||
rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs: rhs_tt });
|
||||
} else {
|
||||
rules.push(MacroRule::Func { lhs, lhs_span, rhs: rhs_tt });
|
||||
}
|
||||
|
|
@ -665,7 +810,7 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<Err
|
|||
None
|
||||
}
|
||||
|
||||
fn check_args_parens(sess: &Session, args: &tokenstream::TokenTree) {
|
||||
fn check_args_parens(sess: &Session, rule_kw: Symbol, args: &tokenstream::TokenTree) {
|
||||
// This does not handle the non-delimited case; that gets handled separately by `check_lhs`.
|
||||
if let tokenstream::TokenTree::Delimited(dspan, _, delim, _) = args
|
||||
&& *delim != Delimiter::Parenthesis
|
||||
|
|
@ -673,10 +818,21 @@ fn check_args_parens(sess: &Session, args: &tokenstream::TokenTree) {
|
|||
sess.dcx().emit_err(errors::MacroArgsBadDelim {
|
||||
span: dspan.entire(),
|
||||
sugg: errors::MacroArgsBadDelimSugg { open: dspan.open, close: dspan.close },
|
||||
rule_kw,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn check_args_empty(sess: &Session, args: &tokenstream::TokenTree) -> Result<(), ErrorGuaranteed> {
|
||||
match args {
|
||||
tokenstream::TokenTree::Delimited(.., delimited) if delimited.is_empty() => Ok(()),
|
||||
_ => {
|
||||
let msg = "`derive` rules do not accept arguments; `derive` must be followed by `()`";
|
||||
Err(sess.dcx().span_err(args.span(), msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_lhs(sess: &Session, node_id: NodeId, lhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed> {
|
||||
let e1 = check_lhs_nt_follows(sess, node_id, lhs);
|
||||
let e2 = check_lhs_no_empty_seq(sess, slice::from_ref(lhs));
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use AttributeDuplicates::*;
|
|||
use AttributeGate::*;
|
||||
use AttributeType::*;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::AttrStyle;
|
||||
use rustc_hir::attrs::EncodeCrossCrate;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
|
@ -132,9 +133,12 @@ pub struct AttributeTemplate {
|
|||
}
|
||||
|
||||
impl AttributeTemplate {
|
||||
pub fn suggestions(&self, inner: bool, name: impl std::fmt::Display) -> Vec<String> {
|
||||
pub fn suggestions(&self, style: AttrStyle, name: impl std::fmt::Display) -> Vec<String> {
|
||||
let mut suggestions = vec![];
|
||||
let inner = if inner { "!" } else { "" };
|
||||
let inner = match style {
|
||||
AttrStyle::Outer => "",
|
||||
AttrStyle::Inner => "!",
|
||||
};
|
||||
if self.word {
|
||||
suggestions.push(format!("#{inner}[{name}]"));
|
||||
}
|
||||
|
|
@ -741,9 +745,8 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
ErrorPreceding, EncodeCrossCrate::No
|
||||
),
|
||||
gated!(
|
||||
no_sanitize, Normal,
|
||||
template!(List: &["address, kcfi, memory, thread"]), DuplicatesOk,
|
||||
EncodeCrossCrate::No, experimental!(no_sanitize)
|
||||
sanitize, Normal, template!(List: &[r#"address = "on|off""#, r#"kernel_address = "on|off""#, r#"cfi = "on|off""#, r#"hwaddress = "on|off""#, r#"kcfi = "on|off""#, r#"memory = "on|off""#, r#"memtag = "on|off""#, r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#]), ErrorPreceding,
|
||||
EncodeCrossCrate::No, sanitize, experimental!(sanitize),
|
||||
),
|
||||
gated!(
|
||||
coverage, Normal, template!(OneOf: &[sym::off, sym::on]),
|
||||
|
|
|
|||
|
|
@ -190,6 +190,9 @@ declare_features! (
|
|||
(removed, no_coverage, "1.74.0", Some(84605), Some("renamed to `coverage_attribute`"), 114656),
|
||||
/// Allows `#[no_debug]`.
|
||||
(removed, no_debug, "1.43.0", Some(29721), Some("removed due to lack of demand"), 69667),
|
||||
// Allows the use of `no_sanitize` attribute.
|
||||
/// The feature was renamed to `sanitize` and the attribute to `#[sanitize(xyz = "on|off")]`
|
||||
(removed, no_sanitize, "CURRENT_RUSTC_VERSION", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681),
|
||||
/// Note: this feature was previously recorded in a separate
|
||||
/// `STABLE_REMOVED` list because it, uniquely, was once stable but was
|
||||
/// then removed. But there was no utility storing it separately, so now
|
||||
|
|
|
|||
|
|
@ -556,6 +556,8 @@ declare_features! (
|
|||
(incomplete, loop_match, "1.90.0", Some(132306)),
|
||||
/// Allow `macro_rules!` attribute rules
|
||||
(unstable, macro_attr, "CURRENT_RUSTC_VERSION", Some(83527)),
|
||||
/// Allow `macro_rules!` derive rules
|
||||
(unstable, macro_derive, "CURRENT_RUSTC_VERSION", Some(143549)),
|
||||
/// Give access to additional metadata about declarative macro meta-variables.
|
||||
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
|
||||
/// Provides a way to concatenate identifiers using metavariable expressions.
|
||||
|
|
@ -592,8 +594,6 @@ declare_features! (
|
|||
(unstable, new_range, "1.86.0", Some(123741)),
|
||||
/// Allows `#![no_core]`.
|
||||
(unstable, no_core, "1.3.0", Some(29639)),
|
||||
/// Allows the use of `no_sanitize` attribute.
|
||||
(unstable, no_sanitize, "1.42.0", Some(39699)),
|
||||
/// Allows using the `non_exhaustive_omitted_patterns` lint.
|
||||
(unstable, non_exhaustive_omitted_patterns_lint, "1.57.0", Some(89554)),
|
||||
/// Allows `for<T>` binders in where-clauses
|
||||
|
|
@ -626,6 +626,8 @@ declare_features! (
|
|||
(unstable, return_type_notation, "1.70.0", Some(109417)),
|
||||
/// Allows `extern "rust-cold"`.
|
||||
(unstable, rust_cold_cc, "1.63.0", Some(97544)),
|
||||
/// Allows the use of the `sanitize` attribute.
|
||||
(unstable, sanitize, "CURRENT_RUSTC_VERSION", Some(39699)),
|
||||
/// Allows the use of SIMD types in functions declared in `extern` blocks.
|
||||
(unstable, simd_ffi, "1.0.0", Some(27731)),
|
||||
/// Allows specialization of implementations (RFC 1210).
|
||||
|
|
|
|||
|
|
@ -205,6 +205,22 @@ pub enum Linkage {
|
|||
WeakODR,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub enum MirDialect {
|
||||
Analysis,
|
||||
Built,
|
||||
Runtime,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub enum MirPhase {
|
||||
Initial,
|
||||
PostCleanup,
|
||||
Optimized,
|
||||
}
|
||||
|
||||
/// Represents parsed *built-in* inert attributes.
|
||||
///
|
||||
/// ## Overview
|
||||
|
|
@ -324,6 +340,9 @@ pub enum AttributeKind {
|
|||
/// Represents `#[coverage(..)]`.
|
||||
Coverage(Span, CoverageAttrKind),
|
||||
|
||||
/// Represents `#[custom_mir]`.
|
||||
CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span),
|
||||
|
||||
///Represents `#[rustc_deny_explicit_impl]`.
|
||||
DenyExplicitImpl(Span),
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ impl AttributeKind {
|
|||
ConstTrait(..) => No,
|
||||
Coroutine(..) => No,
|
||||
Coverage(..) => No,
|
||||
CustomMir(_, _, _) => Yes,
|
||||
DenyExplicitImpl(..) => No,
|
||||
Deprecation { .. } => Yes,
|
||||
DoNotImplementViaObject(..) => No,
|
||||
|
|
|
|||
|
|
@ -290,6 +290,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
| ExprKind::Let(..)
|
||||
| ExprKind::Loop(..)
|
||||
| ExprKind::Match(..) => {}
|
||||
// Do not warn on `as` casts from never to any,
|
||||
// they are sometimes required to appeal typeck.
|
||||
ExprKind::Cast(_, _) => {}
|
||||
// If `expr` is a result of desugaring the try block and is an ok-wrapped
|
||||
// diverging expression (e.g. it arose from desugaring of `try { return }`),
|
||||
// we skip issuing a warning because it is autogenerated code.
|
||||
|
|
|
|||
|
|
@ -782,22 +782,30 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
self.inner.borrow_mut().type_variables().num_vars()
|
||||
}
|
||||
|
||||
pub fn next_ty_vid(&self, span: Span) -> TyVid {
|
||||
self.next_ty_vid_with_origin(TypeVariableOrigin { span, param_def_id: None })
|
||||
}
|
||||
|
||||
pub fn next_ty_vid_with_origin(&self, origin: TypeVariableOrigin) -> TyVid {
|
||||
self.inner.borrow_mut().type_variables().new_var(self.universe(), origin)
|
||||
}
|
||||
|
||||
pub fn next_ty_vid_in_universe(&self, span: Span, universe: ty::UniverseIndex) -> TyVid {
|
||||
let origin = TypeVariableOrigin { span, param_def_id: None };
|
||||
self.inner.borrow_mut().type_variables().new_var(universe, origin)
|
||||
}
|
||||
|
||||
pub fn next_ty_var(&self, span: Span) -> Ty<'tcx> {
|
||||
self.next_ty_var_with_origin(TypeVariableOrigin { span, param_def_id: None })
|
||||
}
|
||||
|
||||
pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
|
||||
let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin);
|
||||
let vid = self.next_ty_vid_with_origin(origin);
|
||||
Ty::new_var(self.tcx, vid)
|
||||
}
|
||||
|
||||
pub fn next_ty_var_id_in_universe(&self, span: Span, universe: ty::UniverseIndex) -> TyVid {
|
||||
let origin = TypeVariableOrigin { span, param_def_id: None };
|
||||
self.inner.borrow_mut().type_variables().new_var(universe, origin)
|
||||
}
|
||||
|
||||
pub fn next_ty_var_in_universe(&self, span: Span, universe: ty::UniverseIndex) -> Ty<'tcx> {
|
||||
let vid = self.next_ty_var_id_in_universe(span, universe);
|
||||
let vid = self.next_ty_vid_in_universe(span, universe);
|
||||
Ty::new_var(self.tcx, vid)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -285,7 +285,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
|
|||
.expecteds
|
||||
.entry(name.name)
|
||||
.and_modify(|v| match v {
|
||||
ExpectedValues::Some(v) if !values_any_specified => {
|
||||
ExpectedValues::Some(v) if !values_any_specified =>
|
||||
{
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
v.extend(values.clone())
|
||||
}
|
||||
ExpectedValues::Some(_) => *v = ExpectedValues::Any,
|
||||
|
|
|
|||
|
|
@ -807,6 +807,7 @@ fn test_unstable_options_tracking_hash() {
|
|||
tracked!(hint_mostly_unused, true);
|
||||
tracked!(human_readable_cgu_names, true);
|
||||
tracked!(incremental_ignore_spans, true);
|
||||
tracked!(indirect_branch_cs_prefix, true);
|
||||
tracked!(inline_mir, Some(true));
|
||||
tracked!(inline_mir_hint_threshold, Some(123));
|
||||
tracked!(inline_mir_threshold, Some(123));
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ impl ClashingExternDeclarations {
|
|||
/// symbol's name.
|
||||
fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName {
|
||||
if let Some((overridden_link_name, overridden_link_name_span)) =
|
||||
tcx.codegen_fn_attrs(fi).link_name.map(|overridden_link_name| {
|
||||
tcx.codegen_fn_attrs(fi).symbol_name.map(|overridden_link_name| {
|
||||
// FIXME: Instead of searching through the attributes again to get span
|
||||
// information, we could have codegen_fn_attrs also give span information back for
|
||||
// where the attribute was defined. However, until this is found to be a
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
|
||||
//! Clippy.
|
||||
|
||||
use rustc_hir::HirId;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy};
|
||||
use rustc_hir::{Expr, ExprKind, HirId};
|
||||
use rustc_middle::ty::{self, ClauseKind, GenericArgsRef, PredicatePolarity, TraitPredicate, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::{Span, sym};
|
||||
|
|
@ -56,25 +56,6 @@ impl LateLintPass<'_> for DefaultHashTypes {
|
|||
}
|
||||
}
|
||||
|
||||
/// Helper function for lints that check for expressions with calls and use typeck results to
|
||||
/// get the `DefId` and `GenericArgsRef` of the function.
|
||||
fn typeck_results_of_method_fn<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &hir::Expr<'_>,
|
||||
) -> Option<(Span, DefId, ty::GenericArgsRef<'tcx>)> {
|
||||
match expr.kind {
|
||||
hir::ExprKind::MethodCall(segment, ..)
|
||||
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
|
||||
{
|
||||
Some((segment.ident.span, def_id, cx.typeck_results().node_args(expr.hir_id)))
|
||||
}
|
||||
_ => match cx.typeck_results().node_type(expr.hir_id).kind() {
|
||||
&ty::FnDef(def_id, args) => Some((expr.span, def_id, args)),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
declare_tool_lint! {
|
||||
/// The `potential_query_instability` lint detects use of methods which can lead to
|
||||
/// potential query instability, such as iterating over a `HashMap`.
|
||||
|
|
@ -101,10 +82,12 @@ declare_tool_lint! {
|
|||
|
||||
declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]);
|
||||
|
||||
impl LateLintPass<'_> for QueryStability {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
|
||||
let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return };
|
||||
if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, args)
|
||||
impl<'tcx> LateLintPass<'tcx> for QueryStability {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let Some((callee_def_id, span, generic_args, _recv, _args)) =
|
||||
get_callee_span_generic_args_and_args(cx, expr)
|
||||
&& let Ok(Some(instance)) =
|
||||
ty::Instance::try_resolve(cx.tcx, cx.typing_env(), callee_def_id, generic_args)
|
||||
{
|
||||
let def_id = instance.def_id();
|
||||
if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
|
||||
|
|
@ -113,7 +96,15 @@ impl LateLintPass<'_> for QueryStability {
|
|||
span,
|
||||
QueryInstability { query: cx.tcx.item_name(def_id) },
|
||||
);
|
||||
} else if has_unstable_into_iter_predicate(cx, callee_def_id, generic_args) {
|
||||
let call_span = span.with_hi(expr.span.hi());
|
||||
cx.emit_span_lint(
|
||||
POTENTIAL_QUERY_INSTABILITY,
|
||||
call_span,
|
||||
QueryInstability { query: sym::into_iter },
|
||||
);
|
||||
}
|
||||
|
||||
if cx.tcx.has_attr(def_id, sym::rustc_lint_untracked_query_information) {
|
||||
cx.emit_span_lint(
|
||||
UNTRACKED_QUERY_INFORMATION,
|
||||
|
|
@ -125,6 +116,64 @@ impl LateLintPass<'_> for QueryStability {
|
|||
}
|
||||
}
|
||||
|
||||
fn has_unstable_into_iter_predicate<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
callee_def_id: DefId,
|
||||
generic_args: GenericArgsRef<'tcx>,
|
||||
) -> bool {
|
||||
let Some(into_iterator_def_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else {
|
||||
return false;
|
||||
};
|
||||
let Some(into_iter_fn_def_id) = cx.tcx.lang_items().into_iter_fn() else {
|
||||
return false;
|
||||
};
|
||||
let predicates = cx.tcx.predicates_of(callee_def_id).instantiate(cx.tcx, generic_args);
|
||||
for (predicate, _) in predicates {
|
||||
let ClauseKind::Trait(TraitPredicate { trait_ref, polarity: PredicatePolarity::Positive }) =
|
||||
predicate.kind().skip_binder()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
// Does the function or method require any of its arguments to implement `IntoIterator`?
|
||||
if trait_ref.def_id != into_iterator_def_id {
|
||||
continue;
|
||||
}
|
||||
let Ok(Some(instance)) =
|
||||
ty::Instance::try_resolve(cx.tcx, cx.typing_env(), into_iter_fn_def_id, trait_ref.args)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
// Does the input type's `IntoIterator` implementation have the
|
||||
// `rustc_lint_query_instability` attribute on its `into_iter` method?
|
||||
if cx.tcx.has_attr(instance.def_id(), sym::rustc_lint_query_instability) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
|
||||
/// `Span`, `GenericArgs`, and arguments. This is a slight augmentation of a similarly named Clippy
|
||||
/// function, `get_callee_generic_args_and_args`.
|
||||
fn get_callee_span_generic_args_and_args<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
) -> Option<(DefId, Span, GenericArgsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> {
|
||||
if let ExprKind::Call(callee, args) = expr.kind
|
||||
&& let callee_ty = cx.typeck_results().expr_ty(callee)
|
||||
&& let ty::FnDef(callee_def_id, generic_args) = callee_ty.kind()
|
||||
{
|
||||
return Some((*callee_def_id, callee.span, generic_args, None, args));
|
||||
}
|
||||
if let ExprKind::MethodCall(segment, recv, args, _) = expr.kind
|
||||
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
{
|
||||
let generic_args = cx.typeck_results().node_args(expr.hir_id);
|
||||
return Some((method_def_id, segment.ident.span, generic_args, Some(recv), args));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
declare_tool_lint! {
|
||||
/// The `usage_of_ty_tykind` lint detects usages of `ty::TyKind::<kind>`,
|
||||
/// where `ty::<kind>` would suffice.
|
||||
|
|
@ -461,33 +510,22 @@ declare_tool_lint! {
|
|||
declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]);
|
||||
|
||||
impl LateLintPass<'_> for Diagnostics {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
|
||||
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
|
||||
let collect_args_tys_and_spans = |args: &[hir::Expr<'_>], reserve_one_extra: bool| {
|
||||
let mut result = Vec::with_capacity(args.len() + usize::from(reserve_one_extra));
|
||||
result.extend(args.iter().map(|arg| (cx.typeck_results().expr_ty(arg), arg.span)));
|
||||
result
|
||||
};
|
||||
// Only check function calls and method calls.
|
||||
let (span, def_id, fn_gen_args, arg_tys_and_spans) = match expr.kind {
|
||||
hir::ExprKind::Call(callee, args) => {
|
||||
match cx.typeck_results().node_type(callee.hir_id).kind() {
|
||||
&ty::FnDef(def_id, fn_gen_args) => {
|
||||
(callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false))
|
||||
}
|
||||
_ => return, // occurs for fns passed as args
|
||||
}
|
||||
}
|
||||
hir::ExprKind::MethodCall(_segment, _recv, args, _span) => {
|
||||
let Some((span, def_id, fn_gen_args)) = typeck_results_of_method_fn(cx, expr)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let mut args = collect_args_tys_and_spans(args, true);
|
||||
args.insert(0, (cx.tcx.types.self_param, _recv.span)); // dummy inserted for `self`
|
||||
(span, def_id, fn_gen_args, args)
|
||||
}
|
||||
_ => return,
|
||||
let Some((def_id, span, fn_gen_args, recv, args)) =
|
||||
get_callee_span_generic_args_and_args(cx, expr)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let mut arg_tys_and_spans = collect_args_tys_and_spans(args, recv.is_some());
|
||||
if let Some(recv) = recv {
|
||||
arg_tys_and_spans.insert(0, (cx.tcx.types.self_param, recv.span)); // dummy inserted for `self`
|
||||
}
|
||||
|
||||
Self::diagnostic_outside_of_impl(cx, span, expr.hir_id, def_id, fn_gen_args);
|
||||
Self::untranslatable_diagnostic(cx, def_id, &arg_tys_and_spans);
|
||||
|
|
@ -496,7 +534,7 @@ impl LateLintPass<'_> for Diagnostics {
|
|||
|
||||
impl Diagnostics {
|
||||
// Is the type `{D,Subd}iagMessage`?
|
||||
fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: MiddleTy<'cx>) -> bool {
|
||||
fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: Ty<'cx>) -> bool {
|
||||
if let Some(adt_def) = ty.ty_adt_def()
|
||||
&& let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
|
||||
&& matches!(name, sym::DiagMessage | sym::SubdiagMessage)
|
||||
|
|
@ -510,7 +548,7 @@ impl Diagnostics {
|
|||
fn untranslatable_diagnostic<'cx>(
|
||||
cx: &LateContext<'cx>,
|
||||
def_id: DefId,
|
||||
arg_tys_and_spans: &[(MiddleTy<'cx>, Span)],
|
||||
arg_tys_and_spans: &[(Ty<'cx>, Span)],
|
||||
) {
|
||||
let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
|
||||
let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates;
|
||||
|
|
|
|||
|
|
@ -2301,18 +2301,18 @@ declare_lint! {
|
|||
|
||||
declare_lint! {
|
||||
/// The `inline_no_sanitize` lint detects incompatible use of
|
||||
/// [`#[inline(always)]`][inline] and [`#[no_sanitize(...)]`][no_sanitize].
|
||||
/// [`#[inline(always)]`][inline] and [`#[sanitize(xyz = "off")]`][sanitize].
|
||||
///
|
||||
/// [inline]: https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute
|
||||
/// [no_sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html
|
||||
/// [sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(no_sanitize)]
|
||||
/// #![feature(sanitize)]
|
||||
///
|
||||
/// #[inline(always)]
|
||||
/// #[no_sanitize(address)]
|
||||
/// #[sanitize(address = "off")]
|
||||
/// fn x() {}
|
||||
///
|
||||
/// fn main() {
|
||||
|
|
@ -2325,11 +2325,11 @@ declare_lint! {
|
|||
/// ### Explanation
|
||||
///
|
||||
/// The use of the [`#[inline(always)]`][inline] attribute prevents the
|
||||
/// the [`#[no_sanitize(...)]`][no_sanitize] attribute from working.
|
||||
/// the [`#[sanitize(xyz = "off")]`][sanitize] attribute from working.
|
||||
/// Consider temporarily removing `inline` attribute.
|
||||
pub INLINE_NO_SANITIZE,
|
||||
Warn,
|
||||
"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`",
|
||||
r#"detects incompatible use of `#[inline(always)]` and `#[sanitize(... = "off")]`"#,
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
|
|
|
|||
|
|
@ -226,7 +226,6 @@ fn main() {
|
|||
rerun_if_changed_anything_in_dir(Path::new("llvm-wrapper"));
|
||||
cfg.file("llvm-wrapper/PassWrapper.cpp")
|
||||
.file("llvm-wrapper/RustWrapper.cpp")
|
||||
.file("llvm-wrapper/ArchiveWrapper.cpp")
|
||||
.file("llvm-wrapper/CoverageMappingWrapper.cpp")
|
||||
.file("llvm-wrapper/SymbolWrapper.cpp")
|
||||
.file("llvm-wrapper/Linker.cpp")
|
||||
|
|
|
|||
|
|
@ -1,208 +0,0 @@
|
|||
#include "LLVMWrapper.h"
|
||||
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/ArchiveWriter.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
struct RustArchiveMember {
|
||||
const char *Filename;
|
||||
const char *Name;
|
||||
Archive::Child Child;
|
||||
|
||||
RustArchiveMember()
|
||||
: Filename(nullptr), Name(nullptr), Child(nullptr, nullptr, nullptr) {}
|
||||
~RustArchiveMember() {}
|
||||
};
|
||||
|
||||
struct RustArchiveIterator {
|
||||
bool First;
|
||||
Archive::child_iterator Cur;
|
||||
Archive::child_iterator End;
|
||||
std::unique_ptr<Error> Err;
|
||||
|
||||
RustArchiveIterator(Archive::child_iterator Cur, Archive::child_iterator End,
|
||||
std::unique_ptr<Error> Err)
|
||||
: First(true), Cur(Cur), End(End), Err(std::move(Err)) {}
|
||||
};
|
||||
|
||||
enum class LLVMRustArchiveKind {
|
||||
GNU,
|
||||
BSD,
|
||||
DARWIN,
|
||||
COFF,
|
||||
AIX_BIG,
|
||||
};
|
||||
|
||||
static Archive::Kind fromRust(LLVMRustArchiveKind Kind) {
|
||||
switch (Kind) {
|
||||
case LLVMRustArchiveKind::GNU:
|
||||
return Archive::K_GNU;
|
||||
case LLVMRustArchiveKind::BSD:
|
||||
return Archive::K_BSD;
|
||||
case LLVMRustArchiveKind::DARWIN:
|
||||
return Archive::K_DARWIN;
|
||||
case LLVMRustArchiveKind::COFF:
|
||||
return Archive::K_COFF;
|
||||
case LLVMRustArchiveKind::AIX_BIG:
|
||||
return Archive::K_AIXBIG;
|
||||
default:
|
||||
report_fatal_error("Bad ArchiveKind.");
|
||||
}
|
||||
}
|
||||
|
||||
typedef OwningBinary<Archive> *LLVMRustArchiveRef;
|
||||
typedef RustArchiveMember *LLVMRustArchiveMemberRef;
|
||||
typedef Archive::Child *LLVMRustArchiveChildRef;
|
||||
typedef Archive::Child const *LLVMRustArchiveChildConstRef;
|
||||
typedef RustArchiveIterator *LLVMRustArchiveIteratorRef;
|
||||
|
||||
extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *Path) {
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr = MemoryBuffer::getFile(
|
||||
Path, /*IsText*/ false, /*RequiresNullTerminator=*/false);
|
||||
if (!BufOr) {
|
||||
LLVMRustSetLastError(BufOr.getError().message().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<Archive>> ArchiveOr =
|
||||
Archive::create(BufOr.get()->getMemBufferRef());
|
||||
|
||||
if (!ArchiveOr) {
|
||||
LLVMRustSetLastError(toString(ArchiveOr.takeError()).c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OwningBinary<Archive> *Ret = new OwningBinary<Archive>(
|
||||
std::move(ArchiveOr.get()), std::move(BufOr.get()));
|
||||
|
||||
return Ret;
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustDestroyArchive(LLVMRustArchiveRef RustArchive) {
|
||||
delete RustArchive;
|
||||
}
|
||||
|
||||
extern "C" LLVMRustArchiveIteratorRef
|
||||
LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) {
|
||||
Archive *Archive = RustArchive->getBinary();
|
||||
std::unique_ptr<Error> Err = std::make_unique<Error>(Error::success());
|
||||
auto Cur = Archive->child_begin(*Err);
|
||||
if (*Err) {
|
||||
LLVMRustSetLastError(toString(std::move(*Err)).c_str());
|
||||
return nullptr;
|
||||
}
|
||||
auto End = Archive->child_end();
|
||||
return new RustArchiveIterator(Cur, End, std::move(Err));
|
||||
}
|
||||
|
||||
extern "C" LLVMRustArchiveChildConstRef
|
||||
LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef RAI) {
|
||||
if (RAI->Cur == RAI->End)
|
||||
return nullptr;
|
||||
|
||||
// Advancing the iterator validates the next child, and this can
|
||||
// uncover an error. LLVM requires that we check all Errors,
|
||||
// so we only advance the iterator if we actually need to fetch
|
||||
// the next child.
|
||||
// This means we must not advance the iterator in the *first* call,
|
||||
// but instead advance it *before* fetching the child in all later calls.
|
||||
if (!RAI->First) {
|
||||
++RAI->Cur;
|
||||
if (*RAI->Err) {
|
||||
LLVMRustSetLastError(toString(std::move(*RAI->Err)).c_str());
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
RAI->First = false;
|
||||
}
|
||||
|
||||
if (RAI->Cur == RAI->End)
|
||||
return nullptr;
|
||||
|
||||
const Archive::Child &Child = *RAI->Cur.operator->();
|
||||
Archive::Child *Ret = new Archive::Child(Child);
|
||||
|
||||
return Ret;
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustArchiveChildFree(LLVMRustArchiveChildRef Child) {
|
||||
delete Child;
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustArchiveIteratorFree(LLVMRustArchiveIteratorRef RAI) {
|
||||
delete RAI;
|
||||
}
|
||||
|
||||
extern "C" const char *
|
||||
LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) {
|
||||
Expected<StringRef> NameOrErr = Child->getName();
|
||||
if (!NameOrErr) {
|
||||
// rustc_codegen_llvm currently doesn't use this error string, but it might
|
||||
// be useful in the future, and in the meantime this tells LLVM that the
|
||||
// error was not ignored and that it shouldn't abort the process.
|
||||
LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str());
|
||||
return nullptr;
|
||||
}
|
||||
StringRef Name = NameOrErr.get();
|
||||
*Size = Name.size();
|
||||
return Name.data();
|
||||
}
|
||||
|
||||
extern "C" LLVMRustArchiveMemberRef
|
||||
LLVMRustArchiveMemberNew(char *Filename, char *Name,
|
||||
LLVMRustArchiveChildRef Child) {
|
||||
RustArchiveMember *Member = new RustArchiveMember;
|
||||
Member->Filename = Filename;
|
||||
Member->Name = Name;
|
||||
if (Child)
|
||||
Member->Child = *Child;
|
||||
return Member;
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) {
|
||||
delete Member;
|
||||
}
|
||||
|
||||
extern "C" LLVMRustResult LLVMRustWriteArchive(
|
||||
char *Dst, size_t NumMembers, const LLVMRustArchiveMemberRef *NewMembers,
|
||||
bool WriteSymbtab, LLVMRustArchiveKind RustKind, bool isEC) {
|
||||
|
||||
std::vector<NewArchiveMember> Members;
|
||||
auto Kind = fromRust(RustKind);
|
||||
|
||||
for (size_t I = 0; I < NumMembers; I++) {
|
||||
auto Member = NewMembers[I];
|
||||
assert(Member->Name);
|
||||
if (Member->Filename) {
|
||||
Expected<NewArchiveMember> MOrErr =
|
||||
NewArchiveMember::getFile(Member->Filename, true);
|
||||
if (!MOrErr) {
|
||||
LLVMRustSetLastError(toString(MOrErr.takeError()).c_str());
|
||||
return LLVMRustResult::Failure;
|
||||
}
|
||||
MOrErr->MemberName = sys::path::filename(MOrErr->MemberName);
|
||||
Members.push_back(std::move(*MOrErr));
|
||||
} else {
|
||||
Expected<NewArchiveMember> MOrErr =
|
||||
NewArchiveMember::getOldMember(Member->Child, true);
|
||||
if (!MOrErr) {
|
||||
LLVMRustSetLastError(toString(MOrErr.takeError()).c_str());
|
||||
return LLVMRustResult::Failure;
|
||||
}
|
||||
Members.push_back(std::move(*MOrErr));
|
||||
}
|
||||
}
|
||||
|
||||
auto SymtabMode = WriteSymbtab ? SymtabWritingMode::NormalSymtab
|
||||
: SymtabWritingMode::NoSymtab;
|
||||
auto Result =
|
||||
writeArchive(Dst, Members, SymtabMode, Kind, true, false, nullptr, isEC);
|
||||
if (!Result)
|
||||
return LLVMRustResult::Success;
|
||||
LLVMRustSetLastError(toString(std::move(Result)).c_str());
|
||||
|
||||
return LLVMRustResult::Failure;
|
||||
}
|
||||
|
|
@ -1460,60 +1460,6 @@ LLVMRustGetDiagInfoKind(LLVMDiagnosticInfoRef DI) {
|
|||
return toRust((DiagnosticKind)unwrap(DI)->getKind());
|
||||
}
|
||||
|
||||
// This is kept distinct from LLVMGetTypeKind, because when
|
||||
// a new type kind is added, the Rust-side enum must be
|
||||
// updated or UB will result.
|
||||
extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) {
|
||||
switch (unwrap(Ty)->getTypeID()) {
|
||||
case Type::VoidTyID:
|
||||
return LLVMVoidTypeKind;
|
||||
case Type::HalfTyID:
|
||||
return LLVMHalfTypeKind;
|
||||
case Type::FloatTyID:
|
||||
return LLVMFloatTypeKind;
|
||||
case Type::DoubleTyID:
|
||||
return LLVMDoubleTypeKind;
|
||||
case Type::X86_FP80TyID:
|
||||
return LLVMX86_FP80TypeKind;
|
||||
case Type::FP128TyID:
|
||||
return LLVMFP128TypeKind;
|
||||
case Type::PPC_FP128TyID:
|
||||
return LLVMPPC_FP128TypeKind;
|
||||
case Type::LabelTyID:
|
||||
return LLVMLabelTypeKind;
|
||||
case Type::MetadataTyID:
|
||||
return LLVMMetadataTypeKind;
|
||||
case Type::IntegerTyID:
|
||||
return LLVMIntegerTypeKind;
|
||||
case Type::FunctionTyID:
|
||||
return LLVMFunctionTypeKind;
|
||||
case Type::StructTyID:
|
||||
return LLVMStructTypeKind;
|
||||
case Type::ArrayTyID:
|
||||
return LLVMArrayTypeKind;
|
||||
case Type::PointerTyID:
|
||||
return LLVMPointerTypeKind;
|
||||
case Type::FixedVectorTyID:
|
||||
return LLVMVectorTypeKind;
|
||||
case Type::TokenTyID:
|
||||
return LLVMTokenTypeKind;
|
||||
case Type::ScalableVectorTyID:
|
||||
return LLVMScalableVectorTypeKind;
|
||||
case Type::BFloatTyID:
|
||||
return LLVMBFloatTypeKind;
|
||||
case Type::X86_AMXTyID:
|
||||
return LLVMX86_AMXTypeKind;
|
||||
default: {
|
||||
std::string error;
|
||||
auto stream = llvm::raw_string_ostream(error);
|
||||
stream << "Rust does not support the TypeID: " << unwrap(Ty)->getTypeID()
|
||||
<< " for the type: " << *unwrap(Ty);
|
||||
stream.flush();
|
||||
report_fatal_error(error.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef)
|
||||
|
||||
extern "C" LLVMSMDiagnosticRef LLVMRustGetSMDiagnostic(LLVMDiagnosticInfoRef DI,
|
||||
|
|
@ -1993,29 +1939,3 @@ extern "C" void LLVMRustSetNoSanitizeHWAddress(LLVMValueRef Global) {
|
|||
MD.NoHWAddress = true;
|
||||
GV.setSanitizerMetadata(MD);
|
||||
}
|
||||
|
||||
enum class LLVMRustTailCallKind {
|
||||
None = 0,
|
||||
Tail = 1,
|
||||
MustTail = 2,
|
||||
NoTail = 3
|
||||
};
|
||||
|
||||
extern "C" void LLVMRustSetTailCallKind(LLVMValueRef Call,
|
||||
LLVMRustTailCallKind Kind) {
|
||||
CallInst *CI = unwrap<CallInst>(Call);
|
||||
switch (Kind) {
|
||||
case LLVMRustTailCallKind::None:
|
||||
CI->setTailCallKind(CallInst::TCK_None);
|
||||
break;
|
||||
case LLVMRustTailCallKind::Tail:
|
||||
CI->setTailCallKind(CallInst::TCK_Tail);
|
||||
break;
|
||||
case LLVMRustTailCallKind::MustTail:
|
||||
CI->setTailCallKind(CallInst::TCK_MustTail);
|
||||
break;
|
||||
case LLVMRustTailCallKind::NoTail:
|
||||
CI->setTailCallKind(CallInst::TCK_NoTail);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use syn::spanned::Spanned;
|
|||
use syn::{Data, Fields, Ident};
|
||||
use synstructure::Structure;
|
||||
|
||||
fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, TokenStream) {
|
||||
fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream) {
|
||||
let string_name = name.to_string();
|
||||
let mut disps = vec![quote! {let mut __printed_anything = false;}];
|
||||
|
||||
|
|
@ -43,7 +43,6 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok
|
|||
#(#disps)*
|
||||
__p.word("}");
|
||||
},
|
||||
quote! { true },
|
||||
)
|
||||
}
|
||||
Fields::Unnamed(fields_unnamed) => {
|
||||
|
|
@ -76,10 +75,9 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok
|
|||
#(#disps)*
|
||||
__p.pclose();
|
||||
},
|
||||
quote! { true },
|
||||
)
|
||||
}
|
||||
Fields::Unit => (quote! {}, quote! { __p.word(#string_name) }, quote! { true }),
|
||||
Fields::Unit => (quote! {}, quote! { __p.word(#string_name) }),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,51 +87,33 @@ pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream {
|
|||
};
|
||||
|
||||
// Must be applied to an enum type.
|
||||
let (code, printed) = match &input.ast().data {
|
||||
let code = match &input.ast().data {
|
||||
Data::Enum(e) => {
|
||||
let (arms, printed) = e
|
||||
let arms = e
|
||||
.variants
|
||||
.iter()
|
||||
.map(|x| {
|
||||
let ident = &x.ident;
|
||||
let (pat, code, printed) = print_fields(ident, &x.fields);
|
||||
let (pat, code) = print_fields(ident, &x.fields);
|
||||
|
||||
(
|
||||
quote! {
|
||||
Self::#ident #pat => {#code}
|
||||
},
|
||||
quote! {
|
||||
Self::#ident #pat => {#printed}
|
||||
},
|
||||
)
|
||||
quote! {
|
||||
Self::#ident #pat => {#code}
|
||||
}
|
||||
})
|
||||
.unzip::<_, _, Vec<_>, Vec<_>>();
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
(
|
||||
quote! {
|
||||
match self {
|
||||
#(#arms)*
|
||||
}
|
||||
},
|
||||
quote! {
|
||||
match self {
|
||||
#(#printed)*
|
||||
}
|
||||
},
|
||||
)
|
||||
quote! {
|
||||
match self {
|
||||
#(#arms)*
|
||||
}
|
||||
}
|
||||
}
|
||||
Data::Struct(s) => {
|
||||
let (pat, code, printed) = print_fields(&input.ast().ident, &s.fields);
|
||||
(
|
||||
quote! {
|
||||
let Self #pat = self;
|
||||
#code
|
||||
},
|
||||
quote! {
|
||||
let Self #pat = self;
|
||||
#printed
|
||||
},
|
||||
)
|
||||
let (pat, code) = print_fields(&input.ast().ident, &s.fields);
|
||||
quote! {
|
||||
let Self #pat = self;
|
||||
#code
|
||||
}
|
||||
}
|
||||
Data::Union(u) => {
|
||||
return span_error(u.union_token.span(), "can't derive PrintAttribute on unions");
|
||||
|
|
@ -144,7 +124,7 @@ pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream {
|
|||
input.gen_impl(quote! {
|
||||
#[allow(unused)]
|
||||
gen impl PrintAttribute for @Self {
|
||||
fn should_render(&self) -> bool { #printed }
|
||||
fn should_render(&self) -> bool { true }
|
||||
fn print_attribute(&self, __p: &mut rustc_ast_pretty::pp::Printer) { #code }
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateS
|
|||
use rustc_session::lint::{self, BuiltinLintDiag};
|
||||
use rustc_session::output::validate_crate_name;
|
||||
use rustc_session::search_paths::PathKind;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
|
||||
use rustc_target::spec::{PanicStrategy, Target};
|
||||
|
|
@ -275,6 +276,10 @@ impl CStore {
|
|||
.filter_map(|(cnum, data)| data.as_deref().map(|data| (cnum, data)))
|
||||
}
|
||||
|
||||
pub fn all_proc_macro_def_ids(&self) -> impl Iterator<Item = DefId> {
|
||||
self.iter_crate_data().flat_map(|(krate, data)| data.proc_macros_for_crate(krate, self))
|
||||
}
|
||||
|
||||
fn push_dependencies_in_postorder(&self, deps: &mut IndexSet<CrateNum>, cnum: CrateNum) {
|
||||
if !deps.contains(&cnum) {
|
||||
let data = self.get_crate_data(cnum);
|
||||
|
|
|
|||
|
|
@ -701,7 +701,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
.link_ordinal
|
||||
.map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
|
||||
|
||||
let name = codegen_fn_attrs.link_name.unwrap_or_else(|| self.tcx.item_name(item));
|
||||
let name = codegen_fn_attrs.symbol_name.unwrap_or_else(|| self.tcx.item_name(item));
|
||||
|
||||
if self.tcx.sess.target.binary_format == BinaryFormat::Elf {
|
||||
let name = name.as_str();
|
||||
|
|
|
|||
|
|
@ -2014,6 +2014,22 @@ impl CrateMetadata {
|
|||
self.root.is_proc_macro_crate()
|
||||
}
|
||||
|
||||
pub(crate) fn proc_macros_for_crate(
|
||||
&self,
|
||||
krate: CrateNum,
|
||||
cstore: &CStore,
|
||||
) -> impl Iterator<Item = DefId> {
|
||||
gen move {
|
||||
for def_id in self.root.proc_macro_data.as_ref().into_iter().flat_map(move |data| {
|
||||
data.macros
|
||||
.decode(CrateMetadataRef { cdata: self, cstore })
|
||||
.map(move |index| DefId { index, krate })
|
||||
}) {
|
||||
yield def_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn name(&self) -> Symbol {
|
||||
self.root.header.name
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,14 +35,10 @@ pub struct CodegenFnAttrs {
|
|||
pub inline: InlineAttr,
|
||||
/// Parsed representation of the `#[optimize]` attribute
|
||||
pub optimize: OptimizeAttr,
|
||||
/// The `#[export_name = "..."]` attribute, indicating a custom symbol a
|
||||
/// function should be exported under
|
||||
pub export_name: Option<Symbol>,
|
||||
/// The `#[link_name = "..."]` attribute, indicating a custom symbol an
|
||||
/// imported function should be imported as. Note that `export_name`
|
||||
/// probably isn't set when this is set, this is for foreign items while
|
||||
/// `#[export_name]` is for Rust-defined functions.
|
||||
pub link_name: Option<Symbol>,
|
||||
/// The name this function will be imported/exported under. This can be set
|
||||
/// using the `#[export_name = "..."]` or `#[link_name = "..."]` attribute
|
||||
/// depending on if this is a function definition or foreign function.
|
||||
pub symbol_name: Option<Symbol>,
|
||||
/// The `#[link_ordinal = "..."]` attribute, indicating an ordinal an
|
||||
/// imported function has in the dynamic library. Note that this must not
|
||||
/// be set when `link_name` is set. This is for foreign items with the
|
||||
|
|
@ -61,8 +57,8 @@ pub struct CodegenFnAttrs {
|
|||
/// The `#[link_section = "..."]` attribute, or what executable section this
|
||||
/// should be placed in.
|
||||
pub link_section: Option<Symbol>,
|
||||
/// The `#[no_sanitize(...)]` attribute. Indicates sanitizers for which
|
||||
/// instrumentation should be disabled inside the annotated function.
|
||||
/// The `#[sanitize(xyz = "off")]` attribute. Indicates sanitizers for which
|
||||
/// instrumentation should be disabled inside the function.
|
||||
pub no_sanitize: SanitizerSet,
|
||||
/// The `#[instruction_set(set)]` attribute. Indicates if the generated code should
|
||||
/// be generated against a specific instruction set. Only usable on architectures which allow
|
||||
|
|
@ -167,8 +163,7 @@ impl CodegenFnAttrs {
|
|||
flags: CodegenFnAttrFlags::empty(),
|
||||
inline: InlineAttr::None,
|
||||
optimize: OptimizeAttr::Default,
|
||||
export_name: None,
|
||||
link_name: None,
|
||||
symbol_name: None,
|
||||
link_ordinal: None,
|
||||
target_features: vec![],
|
||||
safe_target_features: false,
|
||||
|
|
@ -196,7 +191,7 @@ impl CodegenFnAttrs {
|
|||
|
||||
self.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
|
||||
|| self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
|
||||
|| self.export_name.is_some()
|
||||
|| self.symbol_name.is_some()
|
||||
|| match self.linkage {
|
||||
// These are private, so make sure we don't try to consider
|
||||
// them external.
|
||||
|
|
|
|||
|
|
@ -115,48 +115,6 @@ impl MirPhase {
|
|||
MirPhase::Runtime(runtime_phase) => (3, 1 + runtime_phase as usize),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a `MirPhase` from a pair of strings. Panics if this isn't possible for any reason.
|
||||
pub fn parse(dialect: String, phase: Option<String>) -> Self {
|
||||
match &*dialect.to_ascii_lowercase() {
|
||||
"built" => {
|
||||
assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR");
|
||||
MirPhase::Built
|
||||
}
|
||||
"analysis" => Self::Analysis(AnalysisPhase::parse(phase)),
|
||||
"runtime" => Self::Runtime(RuntimePhase::parse(phase)),
|
||||
_ => bug!("Unknown MIR dialect: '{}'", dialect),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AnalysisPhase {
|
||||
pub fn parse(phase: Option<String>) -> Self {
|
||||
let Some(phase) = phase else {
|
||||
return Self::Initial;
|
||||
};
|
||||
|
||||
match &*phase.to_ascii_lowercase() {
|
||||
"initial" => Self::Initial,
|
||||
"post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup,
|
||||
_ => bug!("Unknown analysis phase: '{}'", phase),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimePhase {
|
||||
pub fn parse(phase: Option<String>) -> Self {
|
||||
let Some(phase) = phase else {
|
||||
return Self::Initial;
|
||||
};
|
||||
|
||||
match &*phase.to_ascii_lowercase() {
|
||||
"initial" => Self::Initial,
|
||||
"post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup,
|
||||
"optimized" => Self::Optimized,
|
||||
_ => bug!("Unknown runtime phase: '{}'", phase),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Where a specific `mir::Body` comes from.
|
||||
|
|
|
|||
|
|
@ -929,7 +929,10 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
}
|
||||
Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),
|
||||
Unreachable => write!(fmt, "unreachable"),
|
||||
Drop { place, .. } => write!(fmt, "drop({place:?})"),
|
||||
Drop { place, async_fut: None, .. } => write!(fmt, "drop({place:?})"),
|
||||
Drop { place, async_fut: Some(async_fut), .. } => {
|
||||
write!(fmt, "async drop({place:?}; poll={async_fut:?})")
|
||||
}
|
||||
Call { func, args, destination, .. } => {
|
||||
write!(fmt, "{destination:?} = ")?;
|
||||
write!(fmt, "{func:?}(")?;
|
||||
|
|
|
|||
|
|
@ -531,13 +531,20 @@ macro_rules! make_mir_visitor {
|
|||
unwind: _,
|
||||
replace: _,
|
||||
drop: _,
|
||||
async_fut: _,
|
||||
async_fut,
|
||||
} => {
|
||||
self.visit_place(
|
||||
place,
|
||||
PlaceContext::MutatingUse(MutatingUseContext::Drop),
|
||||
location
|
||||
);
|
||||
if let Some(async_fut) = async_fut {
|
||||
self.visit_local(
|
||||
$(&$mutability)? *async_fut,
|
||||
PlaceContext::MutatingUse(MutatingUseContext::Borrow),
|
||||
location
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TerminatorKind::Call {
|
||||
|
|
|
|||
|
|
@ -343,6 +343,7 @@ trivial! {
|
|||
rustc_span::Symbol,
|
||||
rustc_span::Ident,
|
||||
rustc_target::spec::PanicStrategy,
|
||||
rustc_target::spec::SanitizerSet,
|
||||
rustc_type_ir::Variance,
|
||||
u32,
|
||||
usize,
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ use rustc_session::lint::LintExpectationId;
|
|||
use rustc_span::def_id::LOCAL_CRATE;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol};
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
use rustc_target::spec::{PanicStrategy, SanitizerSet};
|
||||
use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir};
|
||||
|
||||
use crate::infer::canonical::{self, Canonical};
|
||||
|
|
@ -2686,6 +2686,16 @@ rustc_queries! {
|
|||
desc { |tcx| "looking up anon const kind of `{}`", tcx.def_path_str(def_id) }
|
||||
separate_provide_extern
|
||||
}
|
||||
|
||||
/// Checks for the nearest `#[sanitize(xyz = "off")]` or
|
||||
/// `#[sanitize(xyz = "on")]` on this def and any enclosing defs, up to the
|
||||
/// crate root.
|
||||
///
|
||||
/// Returns the set of sanitizers that is explicitly disabled for this def.
|
||||
query disabled_sanitizers_for(key: LocalDefId) -> SanitizerSet {
|
||||
desc { |tcx| "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) }
|
||||
feedable
|
||||
}
|
||||
}
|
||||
|
||||
rustc_with_all_queries! { define_callbacks! }
|
||||
|
|
|
|||
|
|
@ -645,34 +645,29 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> {
|
|||
let parent = Option::<LocalDefId>::decode(self);
|
||||
let tag: u8 = Decodable::decode(self);
|
||||
|
||||
if tag == TAG_PARTIAL_SPAN {
|
||||
return Span::new(BytePos(0), BytePos(0), ctxt, parent);
|
||||
} else if tag == TAG_RELATIVE_SPAN {
|
||||
let dlo = u32::decode(self);
|
||||
let dto = u32::decode(self);
|
||||
let (lo, hi) = match tag {
|
||||
TAG_PARTIAL_SPAN => (BytePos(0), BytePos(0)),
|
||||
TAG_RELATIVE_SPAN => {
|
||||
let dlo = u32::decode(self);
|
||||
let dto = u32::decode(self);
|
||||
|
||||
let enclosing = self.tcx.source_span_untracked(parent.unwrap()).data_untracked();
|
||||
let span = Span::new(
|
||||
enclosing.lo + BytePos::from_u32(dlo),
|
||||
enclosing.lo + BytePos::from_u32(dto),
|
||||
ctxt,
|
||||
parent,
|
||||
);
|
||||
let enclosing = self.tcx.source_span_untracked(parent.unwrap()).data_untracked();
|
||||
(enclosing.lo + BytePos::from_u32(dlo), enclosing.lo + BytePos::from_u32(dto))
|
||||
}
|
||||
TAG_FULL_SPAN => {
|
||||
let file_lo_index = SourceFileIndex::decode(self);
|
||||
let line_lo = usize::decode(self);
|
||||
let col_lo = RelativeBytePos::decode(self);
|
||||
let len = BytePos::decode(self);
|
||||
|
||||
return span;
|
||||
} else {
|
||||
debug_assert_eq!(tag, TAG_FULL_SPAN);
|
||||
}
|
||||
|
||||
let file_lo_index = SourceFileIndex::decode(self);
|
||||
let line_lo = usize::decode(self);
|
||||
let col_lo = RelativeBytePos::decode(self);
|
||||
let len = BytePos::decode(self);
|
||||
|
||||
let file_lo = self.file_index_to_file(file_lo_index);
|
||||
let lo = file_lo.lines()[line_lo - 1] + col_lo;
|
||||
let lo = file_lo.absolute_position(lo);
|
||||
let hi = lo + len;
|
||||
let file_lo = self.file_index_to_file(file_lo_index);
|
||||
let lo = file_lo.lines()[line_lo - 1] + col_lo;
|
||||
let lo = file_lo.absolute_position(lo);
|
||||
let hi = lo + len;
|
||||
(lo, hi)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Span::new(lo, hi, ctxt, parent)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@
|
|||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Attribute, HirId};
|
||||
use rustc_hir::{HirId, attrs};
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
|
|
@ -39,7 +39,8 @@ pub(super) fn build_custom_mir<'tcx>(
|
|||
return_ty: Ty<'tcx>,
|
||||
return_ty_span: Span,
|
||||
span: Span,
|
||||
attr: &Attribute,
|
||||
dialect: Option<attrs::MirDialect>,
|
||||
phase: Option<attrs::MirPhase>,
|
||||
) -> Body<'tcx> {
|
||||
let mut body = Body {
|
||||
basic_blocks: BasicBlocks::new(IndexVec::new()),
|
||||
|
|
@ -72,7 +73,7 @@ pub(super) fn build_custom_mir<'tcx>(
|
|||
inlined_parent_scope: None,
|
||||
local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }),
|
||||
});
|
||||
body.injection_phase = Some(parse_attribute(attr));
|
||||
body.injection_phase = Some(parse_attribute(dialect, phase));
|
||||
|
||||
let mut pctxt = ParseCtxt {
|
||||
tcx,
|
||||
|
|
@ -98,40 +99,38 @@ pub(super) fn build_custom_mir<'tcx>(
|
|||
body
|
||||
}
|
||||
|
||||
fn parse_attribute(attr: &Attribute) -> MirPhase {
|
||||
let meta_items = attr.meta_item_list().unwrap();
|
||||
let mut dialect: Option<String> = None;
|
||||
let mut phase: Option<String> = None;
|
||||
|
||||
// Not handling errors properly for this internal attribute; will just abort on errors.
|
||||
for nested in meta_items {
|
||||
let name = nested.name().unwrap();
|
||||
let value = nested.value_str().unwrap().as_str().to_string();
|
||||
match name.as_str() {
|
||||
"dialect" => {
|
||||
assert!(dialect.is_none());
|
||||
dialect = Some(value);
|
||||
}
|
||||
"phase" => {
|
||||
assert!(phase.is_none());
|
||||
phase = Some(value);
|
||||
}
|
||||
other => {
|
||||
span_bug!(
|
||||
nested.span(),
|
||||
"Unexpected key while parsing custom_mir attribute: '{}'",
|
||||
other
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Turns the arguments passed to `#[custom_mir(..)]` into a proper
|
||||
/// [`MirPhase`]. Panics if this isn't possible for any reason.
|
||||
fn parse_attribute(dialect: Option<attrs::MirDialect>, phase: Option<attrs::MirPhase>) -> MirPhase {
|
||||
let Some(dialect) = dialect else {
|
||||
// Caught during attribute checking.
|
||||
assert!(phase.is_none());
|
||||
return MirPhase::Built;
|
||||
};
|
||||
|
||||
MirPhase::parse(dialect, phase)
|
||||
match dialect {
|
||||
attrs::MirDialect::Built => {
|
||||
// Caught during attribute checking.
|
||||
assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR");
|
||||
MirPhase::Built
|
||||
}
|
||||
attrs::MirDialect::Analysis => match phase {
|
||||
None | Some(attrs::MirPhase::Initial) => MirPhase::Analysis(AnalysisPhase::Initial),
|
||||
|
||||
Some(attrs::MirPhase::PostCleanup) => MirPhase::Analysis(AnalysisPhase::PostCleanup),
|
||||
|
||||
Some(attrs::MirPhase::Optimized) => {
|
||||
// Caught during attribute checking.
|
||||
bug!("`optimized` dialect is not compatible with the `analysis` dialect")
|
||||
}
|
||||
},
|
||||
|
||||
attrs::MirDialect::Runtime => match phase {
|
||||
None | Some(attrs::MirPhase::Initial) => MirPhase::Runtime(RuntimePhase::Initial),
|
||||
Some(attrs::MirPhase::PostCleanup) => MirPhase::Runtime(RuntimePhase::PostCleanup),
|
||||
Some(attrs::MirPhase::Optimized) => MirPhase::Runtime(RuntimePhase::Optimized),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
struct ParseCtxt<'a, 'tcx> {
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@ use rustc_ast::attr;
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sorted_map::SortedIndexMultiMap;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node};
|
||||
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node, find_attr};
|
||||
use rustc_index::bit_set::GrowableBitSet;
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
||||
|
|
@ -479,8 +480,7 @@ fn construct_fn<'tcx>(
|
|||
ty => span_bug!(span_with_body, "unexpected type of body: {ty:?}"),
|
||||
};
|
||||
|
||||
if let Some(custom_mir_attr) =
|
||||
tcx.hir_attrs(fn_id).iter().find(|attr| attr.has_name(sym::custom_mir))
|
||||
if let Some((dialect, phase)) = find_attr!(tcx.hir_attrs(fn_id), AttributeKind::CustomMir(dialect, phase, _) => (dialect, phase))
|
||||
{
|
||||
return custom::build_custom_mir(
|
||||
tcx,
|
||||
|
|
@ -492,7 +492,8 @@ fn construct_fn<'tcx>(
|
|||
return_ty,
|
||||
return_ty_span,
|
||||
span_with_body,
|
||||
custom_mir_attr,
|
||||
dialect.as_ref().map(|(d, _)| *d),
|
||||
phase.as_ref().map(|(p, _)| *p),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ use std::ops::Bound;
|
|||
use rustc_ast::AsmMacro;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::DiagArgValue;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability};
|
||||
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, find_attr};
|
||||
use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
|
||||
use rustc_middle::mir::BorrowKind;
|
||||
use rustc_middle::span_bug;
|
||||
|
|
@ -1157,7 +1158,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
|
|||
// Closures and inline consts are handled by their owner, if it has a body
|
||||
assert!(!tcx.is_typeck_child(def.to_def_id()));
|
||||
// Also, don't safety check custom MIR
|
||||
if tcx.has_attr(def, sym::custom_mir) {
|
||||
if find_attr!(tcx.get_all_attrs(def), AttributeKind::CustomMir(..) => ()).is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
|
||||
use rustc_data_structures::steal::Steal;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::HirId;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{self as hir, HirId, find_attr};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::thir::*;
|
||||
|
|
@ -111,10 +111,8 @@ impl<'tcx> ThirBuildCx<'tcx> {
|
|||
typeck_results,
|
||||
rvalue_scopes: &typeck_results.rvalue_scopes,
|
||||
body_owner: def.to_def_id(),
|
||||
apply_adjustments: tcx
|
||||
.hir_attrs(hir_id)
|
||||
.iter()
|
||||
.all(|attr| !attr.has_name(rustc_span::sym::custom_mir)),
|
||||
apply_adjustments:
|
||||
!find_attr!(tcx.hir_attrs(hir_id), AttributeKind::CustomMir(..) => ()).is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ enum CanonicalizeInputKind {
|
|||
ParamEnv,
|
||||
/// When canonicalizing predicates, we don't keep `'static`. If we're
|
||||
/// currently outside of the trait solver and canonicalize the root goal
|
||||
/// during HIR typeck, we replace each occurance of a region with a
|
||||
/// during HIR typeck, we replace each occurrence of a region with a
|
||||
/// unique region variable. See the comment on `InferCtxt::in_hir_typeck`
|
||||
/// for more details.
|
||||
Predicate { is_hir_typeck_root_goal: bool },
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use rustc_type_ir::inherent::*;
|
|||
use rustc_type_ir::lang_items::TraitSolverLangItem;
|
||||
use rustc_type_ir::solve::{CanonicalResponse, SizedTraitKind};
|
||||
use rustc_type_ir::{
|
||||
self as ty, Interner, Movability, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode,
|
||||
Upcast as _, elaborate,
|
||||
self as ty, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef,
|
||||
TypeVisitableExt as _, TypingMode, Upcast as _, elaborate,
|
||||
};
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
|
|
@ -133,19 +133,26 @@ where
|
|||
cx: I,
|
||||
clause_def_id: I::DefId,
|
||||
goal_def_id: I::DefId,
|
||||
polarity: PredicatePolarity,
|
||||
) -> bool {
|
||||
clause_def_id == goal_def_id
|
||||
// PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
|
||||
// check for a `MetaSized` supertrait being matched against a `Sized` assumption.
|
||||
//
|
||||
// `PointeeSized` bounds are syntactic sugar for a lack of bounds so don't need this.
|
||||
|| (cx.is_lang_item(clause_def_id, TraitSolverLangItem::Sized)
|
||||
|| (polarity == PredicatePolarity::Positive
|
||||
&& cx.is_lang_item(clause_def_id, TraitSolverLangItem::Sized)
|
||||
&& cx.is_lang_item(goal_def_id, TraitSolverLangItem::MetaSized))
|
||||
}
|
||||
|
||||
if let Some(trait_clause) = assumption.as_trait_clause()
|
||||
&& trait_clause.polarity() == goal.predicate.polarity
|
||||
&& trait_def_id_matches(ecx.cx(), trait_clause.def_id(), goal.predicate.def_id())
|
||||
&& trait_def_id_matches(
|
||||
ecx.cx(),
|
||||
trait_clause.def_id(),
|
||||
goal.predicate.def_id(),
|
||||
goal.predicate.polarity,
|
||||
)
|
||||
&& DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
|
||||
goal.predicate.trait_ref.args,
|
||||
trait_clause.skip_binder().trait_ref.args,
|
||||
|
|
@ -168,6 +175,8 @@ where
|
|||
// PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
|
||||
// check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds
|
||||
// are syntactic sugar for a lack of bounds so don't need this.
|
||||
// We don't need to check polarity, `fast_reject_assumption` already rejected non-`Positive`
|
||||
// polarity `Sized` assumptions as matching non-`Positive` `MetaSized` goals.
|
||||
if ecx.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::MetaSized)
|
||||
&& ecx.cx().is_lang_item(trait_clause.def_id(), TraitSolverLangItem::Sized)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -748,9 +748,6 @@ parse_parentheses_with_struct_fields = invalid `struct` delimiters or `fn` call
|
|||
.suggestion_braces_for_struct = if `{$type}` is a struct, use braces as delimiters
|
||||
.suggestion_no_fields_for_fn = if `{$type}` is a function, use the arguments directly
|
||||
|
||||
parse_parenthesized_lifetime = parenthesized lifetime bounds are not supported
|
||||
parse_parenthesized_lifetime_suggestion = remove the parentheses
|
||||
|
||||
parse_path_double_colon = path separator must be a double colon
|
||||
.suggestion = use a double colon instead
|
||||
|
||||
|
|
|
|||
|
|
@ -3163,27 +3163,6 @@ pub(crate) struct ModifierLifetime {
|
|||
pub modifier: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
parse_parenthesized_lifetime_suggestion,
|
||||
applicability = "machine-applicable"
|
||||
)]
|
||||
pub(crate) struct RemoveParens {
|
||||
#[suggestion_part(code = "")]
|
||||
pub lo: Span,
|
||||
#[suggestion_part(code = "")]
|
||||
pub hi: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_parenthesized_lifetime)]
|
||||
pub(crate) struct ParenthesizedLifetime {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[subdiagnostic]
|
||||
pub sugg: RemoveParens,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_underscore_literal_suffix)]
|
||||
pub(crate) struct UnderscoreLiteralSuffix {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ pub(crate) fn lex_token_trees<'psess, 'src>(
|
|||
mut src: &'src str,
|
||||
mut start_pos: BytePos,
|
||||
override_span: Option<Span>,
|
||||
frontmatter_allowed: FrontmatterAllowed,
|
||||
) -> Result<TokenStream, Vec<Diag<'psess>>> {
|
||||
// Skip `#!`, if present.
|
||||
if let Some(shebang_len) = rustc_lexer::strip_shebang(src) {
|
||||
|
|
@ -56,7 +57,7 @@ pub(crate) fn lex_token_trees<'psess, 'src>(
|
|||
start_pos = start_pos + BytePos::from_usize(shebang_len);
|
||||
}
|
||||
|
||||
let cursor = Cursor::new(src, FrontmatterAllowed::Yes);
|
||||
let cursor = Cursor::new(src, frontmatter_allowed);
|
||||
let mut lexer = Lexer {
|
||||
psess,
|
||||
start_pos,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ use rustc_ast::tokenstream::TokenStream;
|
|||
use rustc_ast::{AttrItem, Attribute, MetaItemInner, token};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::{Diag, EmissionGuarantee, FatalError, PResult, pluralize};
|
||||
use rustc_lexer::FrontmatterAllowed;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{FileName, SourceFile, Span};
|
||||
|
|
@ -146,7 +147,7 @@ fn new_parser_from_source_file(
|
|||
source_file: Arc<SourceFile>,
|
||||
) -> Result<Parser<'_>, Vec<Diag<'_>>> {
|
||||
let end_pos = source_file.end_position();
|
||||
let stream = source_file_to_stream(psess, source_file, None)?;
|
||||
let stream = source_file_to_stream(psess, source_file, None, FrontmatterAllowed::Yes)?;
|
||||
let mut parser = Parser::new(psess, stream, None);
|
||||
if parser.token == token::Eof {
|
||||
parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None);
|
||||
|
|
@ -161,7 +162,9 @@ pub fn source_str_to_stream(
|
|||
override_span: Option<Span>,
|
||||
) -> Result<TokenStream, Vec<Diag<'_>>> {
|
||||
let source_file = psess.source_map().new_source_file(name, source);
|
||||
source_file_to_stream(psess, source_file, override_span)
|
||||
// used mainly for `proc_macro` and the likes, not for our parsing purposes, so don't parse
|
||||
// frontmatters as frontmatters.
|
||||
source_file_to_stream(psess, source_file, override_span, FrontmatterAllowed::No)
|
||||
}
|
||||
|
||||
/// Given a source file, produces a sequence of token trees. Returns any buffered errors from
|
||||
|
|
@ -170,6 +173,7 @@ fn source_file_to_stream<'psess>(
|
|||
psess: &'psess ParseSess,
|
||||
source_file: Arc<SourceFile>,
|
||||
override_span: Option<Span>,
|
||||
frontmatter_allowed: FrontmatterAllowed,
|
||||
) -> Result<TokenStream, Vec<Diag<'psess>>> {
|
||||
let src = source_file.src.as_ref().unwrap_or_else(|| {
|
||||
psess.dcx().bug(format!(
|
||||
|
|
@ -178,7 +182,13 @@ fn source_file_to_stream<'psess>(
|
|||
));
|
||||
});
|
||||
|
||||
lexer::lex_token_trees(psess, src.as_str(), source_file.start_pos, override_span)
|
||||
lexer::lex_token_trees(
|
||||
psess,
|
||||
src.as_str(),
|
||||
source_file.start_pos,
|
||||
override_span,
|
||||
frontmatter_allowed,
|
||||
)
|
||||
}
|
||||
|
||||
/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
|
||||
|
|
|
|||
|
|
@ -2397,12 +2397,12 @@ impl<'a> Parser<'a> {
|
|||
let before = self.prev_token;
|
||||
let binder = if self.check_keyword(exp!(For)) {
|
||||
let lo = self.token.span;
|
||||
let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
|
||||
let (bound_vars, _) = self.parse_higher_ranked_binder()?;
|
||||
let span = lo.to(self.prev_token.span);
|
||||
|
||||
self.psess.gated_spans.gate(sym::closure_lifetime_binder, span);
|
||||
|
||||
ClosureBinder::For { span, generic_params: lifetime_defs }
|
||||
ClosureBinder::For { span, generic_params: bound_vars }
|
||||
} else {
|
||||
ClosureBinder::NotPresent
|
||||
};
|
||||
|
|
|
|||
|
|
@ -172,8 +172,11 @@ impl<'a> Parser<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Parses a (possibly empty) list of lifetime and type parameters, possibly including
|
||||
/// a trailing comma and erroneous trailing attributes.
|
||||
/// Parse a (possibly empty) list of generic (lifetime, type, const) parameters.
|
||||
///
|
||||
/// ```ebnf
|
||||
/// GenericParams = (GenericParam ("," GenericParam)* ","?)?
|
||||
/// ```
|
||||
pub(super) fn parse_generic_params(&mut self) -> PResult<'a, ThinVec<ast::GenericParam>> {
|
||||
let mut params = ThinVec::new();
|
||||
let mut done = false;
|
||||
|
|
@ -520,7 +523,7 @@ impl<'a> Parser<'a> {
|
|||
// * `for<'a> Trait1<'a>: Trait2<'a /* ok */>`
|
||||
// * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>`
|
||||
// * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>`
|
||||
let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
|
||||
let (bound_vars, _) = self.parse_higher_ranked_binder()?;
|
||||
|
||||
// Parse type with mandatory colon and (possibly empty) bounds,
|
||||
// or with mandatory equality sign and the second type.
|
||||
|
|
@ -528,7 +531,7 @@ impl<'a> Parser<'a> {
|
|||
if self.eat(exp!(Colon)) {
|
||||
let bounds = self.parse_generic_bounds()?;
|
||||
Ok(ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate {
|
||||
bound_generic_params: lifetime_defs,
|
||||
bound_generic_params: bound_vars,
|
||||
bounded_ty: ty,
|
||||
bounds,
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -308,11 +308,11 @@ impl<'a> Parser<'a> {
|
|||
// Function pointer type or bound list (trait object type) starting with a poly-trait.
|
||||
// `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
|
||||
// `for<'lt> Trait1<'lt> + Trait2 + 'a`
|
||||
let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
|
||||
let (bound_vars, _) = self.parse_higher_ranked_binder()?;
|
||||
if self.check_fn_front_matter(false, Case::Sensitive) {
|
||||
self.parse_ty_fn_ptr(
|
||||
lo,
|
||||
lifetime_defs,
|
||||
bound_vars,
|
||||
Some(self.prev_token.span.shrink_to_lo()),
|
||||
recover_return_sign,
|
||||
)?
|
||||
|
|
@ -326,7 +326,7 @@ impl<'a> Parser<'a> {
|
|||
let path = self.parse_path(PathStyle::Type)?;
|
||||
let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
|
||||
let kind = self.parse_remaining_bounds_path(
|
||||
lifetime_defs,
|
||||
bound_vars,
|
||||
path,
|
||||
lo,
|
||||
parse_plus,
|
||||
|
|
@ -359,7 +359,7 @@ impl<'a> Parser<'a> {
|
|||
let path = self.parse_path(PathStyle::Type)?;
|
||||
let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
|
||||
self.parse_remaining_bounds_path(
|
||||
lifetime_defs,
|
||||
bound_vars,
|
||||
path,
|
||||
lo,
|
||||
parse_plus,
|
||||
|
|
@ -443,7 +443,7 @@ impl<'a> Parser<'a> {
|
|||
let ty = ts.into_iter().next().unwrap();
|
||||
let maybe_bounds = allow_plus == AllowPlus::Yes && self.token.is_like_plus();
|
||||
match ty.kind {
|
||||
// `(TY_BOUND_NOPAREN) + BOUND + ...`.
|
||||
// `"(" BareTraitBound ")" "+" Bound "+" ...`.
|
||||
TyKind::Path(None, path) if maybe_bounds => self.parse_remaining_bounds_path(
|
||||
ThinVec::new(),
|
||||
path,
|
||||
|
|
@ -853,10 +853,13 @@ impl<'a> Parser<'a> {
|
|||
Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds))
|
||||
}
|
||||
|
||||
fn parse_precise_capturing_args(
|
||||
&mut self,
|
||||
) -> PResult<'a, (ThinVec<PreciseCapturingArg>, Span)> {
|
||||
let lo = self.token.span;
|
||||
/// Parse a use-bound aka precise capturing list.
|
||||
///
|
||||
/// ```ebnf
|
||||
/// UseBound = "use" "<" (PreciseCapture ("," PreciseCapture)* ","?)? ">"
|
||||
/// PreciseCapture = "Self" | Ident | Lifetime
|
||||
/// ```
|
||||
fn parse_use_bound(&mut self, lo: Span, parens: ast::Parens) -> PResult<'a, GenericBound> {
|
||||
self.expect_lt()?;
|
||||
let (args, _, _) = self.parse_seq_to_before_tokens(
|
||||
&[exp!(Gt)],
|
||||
|
|
@ -882,7 +885,13 @@ impl<'a> Parser<'a> {
|
|||
},
|
||||
)?;
|
||||
self.expect_gt()?;
|
||||
Ok((args, lo.to(self.prev_token.span)))
|
||||
|
||||
if let ast::Parens::Yes = parens {
|
||||
self.expect(exp!(CloseParen))?;
|
||||
self.report_parenthesized_bound(lo, self.prev_token.span, "precise capturing lists");
|
||||
}
|
||||
|
||||
Ok(GenericBound::Use(args, lo.to(self.prev_token.span)))
|
||||
}
|
||||
|
||||
/// Is a `dyn B0 + ... + Bn` type allowed here?
|
||||
|
|
@ -940,9 +949,10 @@ impl<'a> Parser<'a> {
|
|||
self.parse_generic_bounds_common(AllowPlus::Yes)
|
||||
}
|
||||
|
||||
/// Parses bounds of a type parameter `BOUND + BOUND + ...`, possibly with trailing `+`.
|
||||
/// Parse generic bounds.
|
||||
///
|
||||
/// See `parse_generic_bound` for the `BOUND` grammar.
|
||||
/// Only if `allow_plus` this parses a `+`-separated list of bounds (trailing `+` is admitted).
|
||||
/// Otherwise, this only parses a single bound or none.
|
||||
fn parse_generic_bounds_common(&mut self, allow_plus: AllowPlus) -> PResult<'a, GenericBounds> {
|
||||
let mut bounds = Vec::new();
|
||||
|
||||
|
|
@ -988,48 +998,56 @@ impl<'a> Parser<'a> {
|
|||
|| self.check_keyword(exp!(Use))
|
||||
}
|
||||
|
||||
/// Parses a bound according to the grammar:
|
||||
/// Parse a bound.
|
||||
///
|
||||
/// ```ebnf
|
||||
/// BOUND = TY_BOUND | LT_BOUND
|
||||
/// Bound = LifetimeBound | UseBound | TraitBound
|
||||
/// ```
|
||||
fn parse_generic_bound(&mut self) -> PResult<'a, GenericBound> {
|
||||
let lo = self.token.span;
|
||||
let leading_token = self.prev_token;
|
||||
let lo = self.token.span;
|
||||
|
||||
// We only admit parenthesized *trait* bounds. However, we want to gracefully recover from
|
||||
// other kinds of parenthesized bounds, so parse the opening parenthesis *here*.
|
||||
//
|
||||
// In the future we might want to lift this syntactic restriction and
|
||||
// introduce "`GenericBound::Paren(Box<GenericBound>)`".
|
||||
let parens = if self.eat(exp!(OpenParen)) { ast::Parens::Yes } else { ast::Parens::No };
|
||||
|
||||
let bound = if self.token.is_lifetime() {
|
||||
self.parse_generic_lt_bound(lo, parens)?
|
||||
if self.token.is_lifetime() {
|
||||
self.parse_lifetime_bound(lo, parens)
|
||||
} else if self.eat_keyword(exp!(Use)) {
|
||||
// parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of
|
||||
// lifetimes and ident params (including SelfUpper). These are validated later
|
||||
// for order, duplication, and whether they actually reference params.
|
||||
let use_span = self.prev_token.span;
|
||||
let (args, args_span) = self.parse_precise_capturing_args()?;
|
||||
GenericBound::Use(args, use_span.to(args_span))
|
||||
self.parse_use_bound(lo, parens)
|
||||
} else {
|
||||
self.parse_generic_ty_bound(lo, parens, &leading_token)?
|
||||
};
|
||||
|
||||
Ok(bound)
|
||||
self.parse_trait_bound(lo, parens, &leading_token)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a lifetime ("outlives") bound, e.g. `'a`, according to:
|
||||
/// Parse a lifetime-bound aka outlives-bound.
|
||||
///
|
||||
/// ```ebnf
|
||||
/// LT_BOUND = LIFETIME
|
||||
/// LifetimeBound = Lifetime
|
||||
/// ```
|
||||
fn parse_generic_lt_bound(
|
||||
&mut self,
|
||||
lo: Span,
|
||||
parens: ast::Parens,
|
||||
) -> PResult<'a, GenericBound> {
|
||||
fn parse_lifetime_bound(&mut self, lo: Span, parens: ast::Parens) -> PResult<'a, GenericBound> {
|
||||
let lt = self.expect_lifetime();
|
||||
let bound = GenericBound::Outlives(lt);
|
||||
|
||||
if let ast::Parens::Yes = parens {
|
||||
// FIXME(Centril): Consider not erroring here and accepting `('lt)` instead,
|
||||
// possibly introducing `GenericBound::Paren(Box<GenericBound>)`?
|
||||
self.recover_paren_lifetime(lo)?;
|
||||
self.expect(exp!(CloseParen))?;
|
||||
self.report_parenthesized_bound(lo, self.prev_token.span, "lifetime bounds");
|
||||
}
|
||||
Ok(bound)
|
||||
|
||||
Ok(GenericBound::Outlives(lt))
|
||||
}
|
||||
|
||||
fn report_parenthesized_bound(&self, lo: Span, hi: Span, kind: &str) -> ErrorGuaranteed {
|
||||
let mut diag =
|
||||
self.dcx().struct_span_err(lo.to(hi), format!("{kind} may not be parenthesized"));
|
||||
diag.multipart_suggestion(
|
||||
"remove the parentheses",
|
||||
vec![(lo, String::new()), (hi, String::new())],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
diag.emit()
|
||||
}
|
||||
|
||||
/// Emits an error if any trait bound modifiers were present.
|
||||
|
|
@ -1074,27 +1092,17 @@ impl<'a> Parser<'a> {
|
|||
unreachable!("lifetime bound intercepted in `parse_generic_ty_bound` but no modifiers?")
|
||||
}
|
||||
|
||||
/// Recover on `('lifetime)` with `(` already eaten.
|
||||
fn recover_paren_lifetime(&mut self, lo: Span) -> PResult<'a, ()> {
|
||||
self.expect(exp!(CloseParen))?;
|
||||
let span = lo.to(self.prev_token.span);
|
||||
let sugg = errors::RemoveParens { lo, hi: self.prev_token.span };
|
||||
|
||||
self.dcx().emit_err(errors::ParenthesizedLifetime { span, sugg });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the modifiers that may precede a trait in a bound, e.g. `?Trait` or `[const] Trait`.
|
||||
///
|
||||
/// If no modifiers are present, this does not consume any tokens.
|
||||
///
|
||||
/// ```ebnf
|
||||
/// CONSTNESS = [["["] "const" ["]"]]
|
||||
/// ASYNCNESS = ["async"]
|
||||
/// POLARITY = ["?" | "!"]
|
||||
/// Constness = ("const" | "[" "const" "]")?
|
||||
/// Asyncness = "async"?
|
||||
/// Polarity = ("?" | "!")?
|
||||
/// ```
|
||||
///
|
||||
/// See `parse_generic_ty_bound` for the complete grammar of trait bound modifiers.
|
||||
/// See `parse_trait_bound` for more context.
|
||||
fn parse_trait_bound_modifiers(&mut self) -> PResult<'a, TraitBoundModifiers> {
|
||||
let modifier_lo = self.token.span;
|
||||
let constness = self.parse_bound_constness()?;
|
||||
|
|
@ -1187,20 +1195,21 @@ impl<'a> Parser<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Parses a type bound according to:
|
||||
/// ```ebnf
|
||||
/// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
|
||||
/// TY_BOUND_NOPAREN = [for<GENERIC_PARAMS> CONSTNESS ASYNCNESS | POLARITY] SIMPLE_PATH
|
||||
/// ```
|
||||
/// Parse a trait bound.
|
||||
///
|
||||
/// For example, this grammar accepts `for<'a: 'b> [const] ?m::Trait<'a>`.
|
||||
fn parse_generic_ty_bound(
|
||||
/// ```ebnf
|
||||
/// TraitBound = BareTraitBound | "(" BareTraitBound ")"
|
||||
/// BareTraitBound =
|
||||
/// (HigherRankedBinder Constness Asyncness | Polarity)
|
||||
/// TypePath
|
||||
/// ```
|
||||
fn parse_trait_bound(
|
||||
&mut self,
|
||||
lo: Span,
|
||||
parens: ast::Parens,
|
||||
leading_token: &Token,
|
||||
) -> PResult<'a, GenericBound> {
|
||||
let (mut lifetime_defs, binder_span) = self.parse_late_bound_lifetime_defs()?;
|
||||
let (mut bound_vars, binder_span) = self.parse_higher_ranked_binder()?;
|
||||
|
||||
let modifiers_lo = self.token.span;
|
||||
let modifiers = self.parse_trait_bound_modifiers()?;
|
||||
|
|
@ -1223,11 +1232,11 @@ impl<'a> Parser<'a> {
|
|||
// e.g. `T: for<'a> 'a` or `T: [const] 'a`.
|
||||
if self.token.is_lifetime() {
|
||||
let _: ErrorGuaranteed = self.error_lt_bound_with_modifiers(modifiers, binder_span);
|
||||
return self.parse_generic_lt_bound(lo, parens);
|
||||
return self.parse_lifetime_bound(lo, parens);
|
||||
}
|
||||
|
||||
if let (more_lifetime_defs, Some(binder_span)) = self.parse_late_bound_lifetime_defs()? {
|
||||
lifetime_defs.extend(more_lifetime_defs);
|
||||
if let (more_bound_vars, Some(binder_span)) = self.parse_higher_ranked_binder()? {
|
||||
bound_vars.extend(more_bound_vars);
|
||||
self.dcx().emit_err(errors::BinderBeforeModifiers { binder_span, modifiers_span });
|
||||
}
|
||||
|
||||
|
|
@ -1287,7 +1296,7 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
|
||||
if self.may_recover() && self.token == TokenKind::OpenParen {
|
||||
self.recover_fn_trait_with_lifetime_params(&mut path, &mut lifetime_defs)?;
|
||||
self.recover_fn_trait_with_lifetime_params(&mut path, &mut bound_vars)?;
|
||||
}
|
||||
|
||||
if let ast::Parens::Yes = parens {
|
||||
|
|
@ -1310,7 +1319,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
let poly_trait =
|
||||
PolyTraitRef::new(lifetime_defs, path, modifiers, lo.to(self.prev_token.span), parens);
|
||||
PolyTraitRef::new(bound_vars, path, modifiers, lo.to(self.prev_token.span), parens);
|
||||
Ok(GenericBound::Trait(poly_trait))
|
||||
}
|
||||
|
||||
|
|
@ -1349,8 +1358,12 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Optionally parses `for<$generic_params>`.
|
||||
pub(super) fn parse_late_bound_lifetime_defs(
|
||||
/// Parse an optional higher-ranked binder.
|
||||
///
|
||||
/// ```ebnf
|
||||
/// HigherRankedBinder = ("for" "<" GenericParams ">")?
|
||||
/// ```
|
||||
pub(super) fn parse_higher_ranked_binder(
|
||||
&mut self,
|
||||
) -> PResult<'a, (ThinVec<GenericParam>, Option<Span>)> {
|
||||
if self.eat_keyword(exp!(For)) {
|
||||
|
|
|
|||
|
|
@ -74,6 +74,16 @@ passes_const_stable_not_stable =
|
|||
attribute `#[rustc_const_stable]` can only be applied to functions that are declared `#[stable]`
|
||||
.label = attribute specified here
|
||||
|
||||
passes_custom_mir_incompatible_dialect_and_phase =
|
||||
The {$dialect} dialect is not compatible with the {$phase} phase
|
||||
.dialect_span = this dialect...
|
||||
.phase_span = ... is not compatible with this phase
|
||||
|
||||
passes_custom_mir_phase_requires_dialect =
|
||||
`dialect` key required
|
||||
.phase_span = `phase` argument requires a `dialect` argument
|
||||
|
||||
|
||||
passes_dead_codes =
|
||||
{ $multiple ->
|
||||
*[true] multiple {$descr}s are
|
||||
|
|
@ -422,10 +432,6 @@ passes_must_not_suspend =
|
|||
`must_not_suspend` attribute should be applied to a struct, enum, union, or trait
|
||||
.label = is not a struct, enum, union, or trait
|
||||
|
||||
passes_must_use_no_effect =
|
||||
`#[must_use]` has no effect when applied to {$target}
|
||||
.suggestion = remove the attribute
|
||||
|
||||
passes_no_link =
|
||||
attribute should be applied to an `extern crate` item
|
||||
.label = not an `extern crate` item
|
||||
|
|
@ -444,10 +450,6 @@ passes_no_main_function =
|
|||
.teach_note = If you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/
|
||||
.non_function_main = non-function item at `crate::main` is found
|
||||
|
||||
passes_no_sanitize =
|
||||
`#[no_sanitize({$attr_str})]` should be applied to {$accepted_kind}
|
||||
.label = not {$accepted_kind}
|
||||
|
||||
passes_non_exhaustive_with_default_field_values =
|
||||
`#[non_exhaustive]` can't be used to annotate items with default field values
|
||||
.label = this struct has default field values
|
||||
|
|
@ -542,6 +544,12 @@ passes_rustc_pub_transparent =
|
|||
attribute should be applied to `#[repr(transparent)]` types
|
||||
.label = not a `#[repr(transparent)]` type
|
||||
|
||||
passes_sanitize_attribute_not_allowed =
|
||||
sanitize attribute not allowed here
|
||||
.not_fn_impl_mod = not a function, impl block, or module
|
||||
.no_body = function has no body
|
||||
.help = sanitize attribute can be applied to a function (with body), impl block, or module
|
||||
|
||||
passes_should_be_applied_to_fn =
|
||||
attribute should be applied to a function definition
|
||||
.label = {$on_crate ->
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use rustc_feature::{
|
|||
ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP,
|
||||
BuiltinAttribute,
|
||||
};
|
||||
use rustc_hir::attrs::{AttributeKind, InlineAttr, ReprAttr};
|
||||
use rustc_hir::attrs::{AttributeKind, InlineAttr, MirDialect, MirPhase, ReprAttr};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalModDefId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
|
|
@ -194,8 +194,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => {
|
||||
self.check_may_dangle(hir_id, *attr_span)
|
||||
}
|
||||
Attribute::Parsed(AttributeKind::MustUse { span, .. }) => {
|
||||
self.check_must_use(hir_id, *span, target)
|
||||
&Attribute::Parsed(AttributeKind::CustomMir(dialect, phase, attr_span)) => {
|
||||
self.check_custom_mir(dialect, phase, attr_span)
|
||||
}
|
||||
Attribute::Parsed(
|
||||
AttributeKind::BodyStability { .. }
|
||||
|
|
@ -246,9 +246,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
| AttributeKind::Coverage (..)
|
||||
| AttributeKind::ShouldPanic { .. }
|
||||
| AttributeKind::Coroutine(..)
|
||||
| AttributeKind::Linkage(..),
|
||||
| AttributeKind::Linkage(..)
|
||||
| AttributeKind::MustUse { .. },
|
||||
) => { /* do nothing */ }
|
||||
|
||||
Attribute::Unparsed(attr_item) => {
|
||||
style = Some(attr_item.style);
|
||||
match attr.path().as_slice() {
|
||||
|
|
@ -258,8 +258,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
[sym::diagnostic, sym::on_unimplemented, ..] => {
|
||||
self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
|
||||
}
|
||||
[sym::no_sanitize, ..] => {
|
||||
self.check_no_sanitize(attr, span, target)
|
||||
[sym::sanitize, ..] => {
|
||||
self.check_sanitize(attr, span, target)
|
||||
}
|
||||
[sym::thread_local, ..] => self.check_thread_local(attr, span, target),
|
||||
[sym::doc, ..] => self.check_doc_attrs(
|
||||
|
|
@ -331,8 +331,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
| sym::panic_handler
|
||||
| sym::lang
|
||||
| sym::needs_allocator
|
||||
| sym::default_lib_allocator
|
||||
| sym::custom_mir,
|
||||
| sym::default_lib_allocator,
|
||||
..
|
||||
] => {}
|
||||
[name, rest@..] => {
|
||||
|
|
@ -482,39 +481,43 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_no_sanitize(&self, attr: &Attribute, span: Span, target: Target) {
|
||||
/// Checks that the `#[sanitize(..)]` attribute is applied to a
|
||||
/// function/closure/method, or to an impl block or module.
|
||||
fn check_sanitize(&self, attr: &Attribute, target_span: Span, target: Target) {
|
||||
let mut not_fn_impl_mod = None;
|
||||
let mut no_body = None;
|
||||
|
||||
if let Some(list) = attr.meta_item_list() {
|
||||
for item in list.iter() {
|
||||
let sym = item.name();
|
||||
match sym {
|
||||
Some(s @ sym::address | s @ sym::hwaddress) => {
|
||||
let is_valid =
|
||||
matches!(target, Target::Fn | Target::Method(..) | Target::Static);
|
||||
if !is_valid {
|
||||
self.dcx().emit_err(errors::NoSanitize {
|
||||
attr_span: item.span(),
|
||||
defn_span: span,
|
||||
accepted_kind: "a function or static",
|
||||
attr_str: s.as_str(),
|
||||
});
|
||||
}
|
||||
let MetaItemInner::MetaItem(set) = item else {
|
||||
return;
|
||||
};
|
||||
let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
|
||||
match target {
|
||||
Target::Fn
|
||||
| Target::Closure
|
||||
| Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)
|
||||
| Target::Impl { .. }
|
||||
| Target::Mod => return,
|
||||
Target::Static if matches!(segments.as_slice(), [sym::address]) => return,
|
||||
|
||||
// These are "functions", but they aren't allowed because they don't
|
||||
// have a body, so the usual explanation would be confusing.
|
||||
Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
|
||||
no_body = Some(target_span);
|
||||
}
|
||||
|
||||
_ => {
|
||||
let is_valid = matches!(target, Target::Fn | Target::Method(..));
|
||||
if !is_valid {
|
||||
self.dcx().emit_err(errors::NoSanitize {
|
||||
attr_span: item.span(),
|
||||
defn_span: span,
|
||||
accepted_kind: "a function",
|
||||
attr_str: &match sym {
|
||||
Some(name) => name.to_string(),
|
||||
None => "...".to_string(),
|
||||
},
|
||||
});
|
||||
}
|
||||
not_fn_impl_mod = Some(target_span);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.dcx().emit_err(errors::SanitizeAttributeNotAllowed {
|
||||
attr_span: attr.span(),
|
||||
not_fn_impl_mod,
|
||||
no_body,
|
||||
help: (),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -561,7 +564,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if `#[collapse_debuginfo]` is applied to a macro.
|
||||
fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) {
|
||||
match target {
|
||||
|
|
@ -1259,41 +1261,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Warns against some misuses of `#[must_use]`
|
||||
fn check_must_use(&self, hir_id: HirId, attr_span: Span, target: Target) {
|
||||
if matches!(
|
||||
target,
|
||||
Target::Fn
|
||||
| Target::Enum
|
||||
| Target::Struct
|
||||
| Target::Union
|
||||
| Target::Method(MethodKind::Trait { body: false } | MethodKind::Inherent)
|
||||
| Target::ForeignFn
|
||||
// `impl Trait` in return position can trip
|
||||
// `unused_must_use` if `Trait` is marked as
|
||||
// `#[must_use]`
|
||||
| Target::Trait
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// `#[must_use]` can be applied to a trait method definition with a default body
|
||||
if let Target::Method(MethodKind::Trait { body: true }) = target
|
||||
&& let parent_def_id = self.tcx.hir_get_parent_item(hir_id).def_id
|
||||
&& let containing_item = self.tcx.hir_expect_item(parent_def_id)
|
||||
&& let hir::ItemKind::Trait(..) = containing_item.kind
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr_span,
|
||||
errors::MustUseNoEffect { target: target.plural_name(), attr_span },
|
||||
);
|
||||
}
|
||||
|
||||
/// Checks if `#[must_not_suspend]` is applied to a struct, enum, union, or trait.
|
||||
fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) {
|
||||
match target {
|
||||
|
|
@ -2113,6 +2080,48 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span });
|
||||
};
|
||||
}
|
||||
|
||||
fn check_custom_mir(
|
||||
&self,
|
||||
dialect: Option<(MirDialect, Span)>,
|
||||
phase: Option<(MirPhase, Span)>,
|
||||
attr_span: Span,
|
||||
) {
|
||||
let Some((dialect, dialect_span)) = dialect else {
|
||||
if let Some((_, phase_span)) = phase {
|
||||
self.dcx()
|
||||
.emit_err(errors::CustomMirPhaseRequiresDialect { attr_span, phase_span });
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
match dialect {
|
||||
MirDialect::Analysis => {
|
||||
if let Some((MirPhase::Optimized, phase_span)) = phase {
|
||||
self.dcx().emit_err(errors::CustomMirIncompatibleDialectAndPhase {
|
||||
dialect,
|
||||
phase: MirPhase::Optimized,
|
||||
attr_span,
|
||||
dialect_span,
|
||||
phase_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
MirDialect::Built => {
|
||||
if let Some((phase, phase_span)) = phase {
|
||||
self.dcx().emit_err(errors::CustomMirIncompatibleDialectAndPhase {
|
||||
dialect,
|
||||
phase,
|
||||
attr_span,
|
||||
dialect_span,
|
||||
phase_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
MirDialect::Runtime => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use rustc_errors::{
|
|||
MultiSpan, Subdiagnostic,
|
||||
};
|
||||
use rustc_hir::Target;
|
||||
use rustc_hir::attrs::{MirDialect, MirPhase};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::ty::{MainDefinition, Ty};
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol};
|
||||
|
|
@ -357,14 +358,6 @@ pub(crate) struct BothFfiConstAndPure {
|
|||
pub attr_span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_must_use_no_effect)]
|
||||
pub(crate) struct MustUseNoEffect {
|
||||
pub target: &'static str,
|
||||
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
|
||||
pub attr_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_must_not_suspend)]
|
||||
pub(crate) struct MustNotSuspend {
|
||||
|
|
@ -1488,15 +1481,21 @@ pub(crate) struct AttrCrateLevelOnlySugg {
|
|||
pub attr: Span,
|
||||
}
|
||||
|
||||
/// "sanitize attribute not allowed here"
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_no_sanitize)]
|
||||
pub(crate) struct NoSanitize<'a> {
|
||||
#[diag(passes_sanitize_attribute_not_allowed)]
|
||||
pub(crate) struct SanitizeAttributeNotAllowed {
|
||||
#[primary_span]
|
||||
pub attr_span: Span,
|
||||
#[label]
|
||||
pub defn_span: Span,
|
||||
pub accepted_kind: &'a str,
|
||||
pub attr_str: &'a str,
|
||||
/// "not a function, impl block, or module"
|
||||
#[label(passes_not_fn_impl_mod)]
|
||||
pub not_fn_impl_mod: Option<Span>,
|
||||
/// "function has no body"
|
||||
#[label(passes_no_body)]
|
||||
pub no_body: Option<Span>,
|
||||
/// "sanitize attribute can be applied to a function (with body), impl block, or module"
|
||||
#[help]
|
||||
pub help: (),
|
||||
}
|
||||
|
||||
// FIXME(jdonszelmann): move back to rustc_attr
|
||||
|
|
@ -1570,3 +1569,25 @@ pub(crate) struct ReprAlignShouldBeAlign {
|
|||
pub span: Span,
|
||||
pub item: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_custom_mir_phase_requires_dialect)]
|
||||
pub(crate) struct CustomMirPhaseRequiresDialect {
|
||||
#[primary_span]
|
||||
pub attr_span: Span,
|
||||
#[label]
|
||||
pub phase_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_custom_mir_incompatible_dialect_and_phase)]
|
||||
pub(crate) struct CustomMirIncompatibleDialectAndPhase {
|
||||
pub dialect: MirDialect,
|
||||
pub phase: MirPhase,
|
||||
#[primary_span]
|
||||
pub attr_span: Span,
|
||||
#[label]
|
||||
pub dialect_span: Span,
|
||||
#[label]
|
||||
pub phase_span: Span,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,6 +93,9 @@ resolve_consider_adding_a_derive =
|
|||
resolve_consider_adding_macro_export =
|
||||
consider adding a `#[macro_export]` to the macro in the imported module
|
||||
|
||||
resolve_consider_marking_as_pub_crate =
|
||||
in case you want to use the macro within this crate only, reduce the visibility to `pub(crate)`
|
||||
|
||||
resolve_consider_declaring_with_pub =
|
||||
consider declaring type or module `{$ident}` with `pub`
|
||||
|
||||
|
|
@ -249,7 +252,7 @@ resolve_macro_cannot_use_as_attr =
|
|||
`{$ident}` exists, but has no `attr` rules
|
||||
|
||||
resolve_macro_cannot_use_as_derive =
|
||||
`{$ident}` exists, but a declarative macro cannot be used as a derive macro
|
||||
`{$ident}` exists, but has no `derive` rules
|
||||
|
||||
resolve_macro_defined_later =
|
||||
a macro with the same name exists, but it appears later
|
||||
|
|
|
|||
|
|
@ -210,6 +210,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Add every proc macro accessible from the current crate to the `macro_map` so diagnostics can
|
||||
/// find them for suggestions.
|
||||
pub(crate) fn register_macros_for_all_crates(&mut self) {
|
||||
if !self.all_crate_macros_already_registered {
|
||||
for def_id in self.cstore().all_proc_macro_def_ids() {
|
||||
self.get_macro_by_def_id(def_id);
|
||||
}
|
||||
self.all_crate_macros_already_registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build_reduced_graph(
|
||||
&mut self,
|
||||
fragment: &AstFragment,
|
||||
|
|
@ -474,6 +485,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
root_span,
|
||||
root_id,
|
||||
vis,
|
||||
vis_span: item.vis.span,
|
||||
});
|
||||
|
||||
self.r.indeterminate_imports.push(import);
|
||||
|
|
@ -966,6 +978,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
span: item.span,
|
||||
module_path: Vec::new(),
|
||||
vis,
|
||||
vis_span: item.vis.span,
|
||||
});
|
||||
if used {
|
||||
self.r.import_use_map.insert(import, Used::Other);
|
||||
|
|
@ -1100,6 +1113,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
span,
|
||||
module_path: Vec::new(),
|
||||
vis: Visibility::Restricted(CRATE_DEF_ID),
|
||||
vis_span: item.vis.span,
|
||||
})
|
||||
};
|
||||
|
||||
|
|
@ -1270,6 +1284,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
span,
|
||||
module_path: Vec::new(),
|
||||
vis,
|
||||
vis_span: item.vis.span,
|
||||
});
|
||||
self.r.import_use_map.insert(import, Used::Other);
|
||||
let import_binding = self.r.import(binding, import);
|
||||
|
|
|
|||
|
|
@ -1469,33 +1469,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
krate: &Crate,
|
||||
sugg_span: Option<Span>,
|
||||
) {
|
||||
// Bring imported but unused `derive` macros into `macro_map` so we ensure they can be used
|
||||
// for suggestions.
|
||||
self.cm().visit_scopes(
|
||||
ScopeSet::Macro(MacroKind::Derive),
|
||||
&parent_scope,
|
||||
ident.span.ctxt(),
|
||||
|this, scope, _use_prelude, _ctxt| {
|
||||
let Scope::Module(m, _) = scope else {
|
||||
return None;
|
||||
};
|
||||
for (_, resolution) in this.resolutions(m).borrow().iter() {
|
||||
let Some(binding) = resolution.borrow().best_binding() else {
|
||||
continue;
|
||||
};
|
||||
let Res::Def(DefKind::Macro(kinds), def_id) = binding.res() else {
|
||||
continue;
|
||||
};
|
||||
if !kinds.intersects(MacroKinds::ATTR | MacroKinds::DERIVE) {
|
||||
continue;
|
||||
}
|
||||
// By doing this all *imported* macros get added to the `macro_map` even if they
|
||||
// are *unused*, which makes the later suggestions find them and work.
|
||||
let _ = this.get_macro_by_def_id(def_id);
|
||||
}
|
||||
None::<()>
|
||||
},
|
||||
);
|
||||
// Bring all unused `derive` macros into `macro_map` so we ensure they can be used for
|
||||
// suggestions.
|
||||
self.register_macros_for_all_crates();
|
||||
|
||||
let is_expected =
|
||||
&|res: Res| res.macro_kinds().is_some_and(|k| k.contains(macro_kind.into()));
|
||||
|
|
|
|||
|
|
@ -775,6 +775,17 @@ pub(crate) struct ConsiderAddingMacroExport {
|
|||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[suggestion(
|
||||
resolve_consider_marking_as_pub_crate,
|
||||
code = "pub(crate)",
|
||||
applicability = "maybe-incorrect"
|
||||
)]
|
||||
pub(crate) struct ConsiderMarkingAsPubCrate {
|
||||
#[primary_span]
|
||||
pub(crate) vis_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[note(resolve_consider_marking_as_pub)]
|
||||
pub(crate) struct ConsiderMarkingAsPub {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ use crate::diagnostics::{DiagMode, Suggestion, import_candidates};
|
|||
use crate::errors::{
|
||||
CannotBeReexportedCratePublic, CannotBeReexportedCratePublicNS, CannotBeReexportedPrivate,
|
||||
CannotBeReexportedPrivateNS, CannotDetermineImportResolution, CannotGlobImportAllCrates,
|
||||
ConsiderAddingMacroExport, ConsiderMarkingAsPub,
|
||||
ConsiderAddingMacroExport, ConsiderMarkingAsPub, ConsiderMarkingAsPubCrate,
|
||||
};
|
||||
use crate::{
|
||||
AmbiguityError, AmbiguityKind, BindingKey, CmResolver, Determinacy, Finalize, ImportSuggestion,
|
||||
|
|
@ -184,6 +184,9 @@ pub(crate) struct ImportData<'ra> {
|
|||
/// |`use foo` | `ModuleOrUniformRoot::CurrentScope` | - |
|
||||
pub imported_module: Cell<Option<ModuleOrUniformRoot<'ra>>>,
|
||||
pub vis: Visibility,
|
||||
|
||||
/// Span of the visibility.
|
||||
pub vis_span: Span,
|
||||
}
|
||||
|
||||
/// All imports are unique and allocated on a same arena,
|
||||
|
|
@ -866,7 +869,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
}
|
||||
ImportKind::Glob { .. } => {
|
||||
// FIXME: Use mutable resolver directly as a hack, this should be an output of
|
||||
// specualtive resolution.
|
||||
// speculative resolution.
|
||||
self.get_mut_unchecked().resolve_glob_import(import);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -903,7 +906,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
// We need the `target`, `source` can be extracted.
|
||||
let imported_binding = this.import(binding, import);
|
||||
// FIXME: Use mutable resolver directly as a hack, this should be an output of
|
||||
// specualtive resolution.
|
||||
// speculative resolution.
|
||||
this.get_mut_unchecked().define_binding_local(
|
||||
parent,
|
||||
target,
|
||||
|
|
@ -917,7 +920,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
if target.name != kw::Underscore {
|
||||
let key = BindingKey::new(target, ns);
|
||||
// FIXME: Use mutable resolver directly as a hack, this should be an output of
|
||||
// specualtive resolution.
|
||||
// speculative resolution.
|
||||
this.get_mut_unchecked().update_local_resolution(
|
||||
parent,
|
||||
key,
|
||||
|
|
@ -1368,6 +1371,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
err.subdiagnostic( ConsiderAddingMacroExport {
|
||||
span: binding.span,
|
||||
});
|
||||
err.subdiagnostic( ConsiderMarkingAsPubCrate {
|
||||
vis_span: import.vis_span,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
err.subdiagnostic( ConsiderMarkingAsPub {
|
||||
|
|
|
|||
|
|
@ -4270,7 +4270,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
if path.len() == 2
|
||||
&& let [segment] = prefix_path
|
||||
{
|
||||
// Delay to check whether methond name is an associated function or not
|
||||
// Delay to check whether method name is an associated function or not
|
||||
// ```
|
||||
// let foo = Foo {};
|
||||
// foo::bar(); // possibly suggest to foo.bar();
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue