Auto merge of #140043 - ChrisDenton:rollup-vwf0s9j, r=ChrisDenton
Rollup of 8 pull requests Successful merges: - #138934 (support config extensions) - #139091 (Rewrite on_unimplemented format string parser.) - #139753 (Make `#[naked]` an unsafe attribute) - #139762 (Don't assemble non-env/bound candidates if projection is rigid) - #139834 (Don't canonicalize crate paths) - #139868 (Move `pal::env` to `std::sys::env_consts`) - #139978 (Add citool command for generating a test dashboard) - #139995 (Clean UI tests 4 of n) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
49e5e4e3a5
138 changed files with 2861 additions and 1624 deletions
|
@ -19,6 +19,14 @@
|
|||
# Note that this has no default value (x.py uses the defaults in `bootstrap.example.toml`).
|
||||
#profile = <none>
|
||||
|
||||
# Inherits configuration values from different configuration files (a.k.a. config extensions).
|
||||
# Supports absolute paths, and uses the current directory (where the bootstrap was invoked)
|
||||
# as the base if the given path is not absolute.
|
||||
#
|
||||
# The overriding logic follows a right-to-left order. For example, in `include = ["a.toml", "b.toml"]`,
|
||||
# extension `b.toml` overrides `a.toml`. Also, parent extensions always overrides the inner ones.
|
||||
#include = []
|
||||
|
||||
# Keeps track of major changes made to this configuration.
|
||||
#
|
||||
# This value also represents ID of the PR that caused major changes. Meaning,
|
||||
|
|
|
@ -247,9 +247,9 @@ builtin_macros_multiple_defaults = multiple declared defaults
|
|||
.suggestion = make `{$ident}` default
|
||||
|
||||
builtin_macros_naked_functions_testing_attribute =
|
||||
cannot use `#[naked]` with testing attributes
|
||||
cannot use `#[unsafe(naked)]` with testing attributes
|
||||
.label = function marked with testing attribute here
|
||||
.naked_attribute = `#[naked]` is incompatible with testing attributes
|
||||
.naked_attribute = `#[unsafe(naked)]` is incompatible with testing attributes
|
||||
|
||||
builtin_macros_no_default_variant = `#[derive(Default)]` on enum with no `#[default]`
|
||||
.label = this enum needs a unit variant marked with `#[default]`
|
||||
|
|
|
@ -387,11 +387,9 @@ global_asm! {
|
|||
}
|
||||
|
||||
#[cfg(all(not(jit), target_arch = "x86_64"))]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn naked_test() {
|
||||
unsafe {
|
||||
naked_asm!("ret");
|
||||
}
|
||||
naked_asm!("ret")
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
|
|
@ -11,7 +11,7 @@ Erroneous code example:
|
|||
|
||||
```compile_fail,E0736
|
||||
#[inline]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
fn foo() {}
|
||||
```
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ Erroneous code example:
|
|||
```compile_fail,E0787
|
||||
#![feature(naked_functions)]
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn f() -> u32 {
|
||||
42
|
||||
}
|
||||
|
|
|
@ -517,7 +517,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
|
||||
// Linking:
|
||||
gated!(
|
||||
naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No,
|
||||
unsafe naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No,
|
||||
naked_functions, experimental!(naked)
|
||||
),
|
||||
|
||||
|
|
|
@ -427,12 +427,21 @@ impl<'a> CrateLocator<'a> {
|
|||
|
||||
let (rlibs, rmetas, dylibs) =
|
||||
candidates.entry(hash.to_string()).or_default();
|
||||
let path =
|
||||
try_canonicalize(&spf.path).unwrap_or_else(|_| spf.path.to_path_buf());
|
||||
{
|
||||
// As a perforamnce optimisation we canonicalize the path and skip
|
||||
// ones we've already seeen. This allows us to ignore crates
|
||||
// we know are exactual equal to ones we've already found.
|
||||
// Going to the same crate through different symlinks does not change the result.
|
||||
let path = try_canonicalize(&spf.path)
|
||||
.unwrap_or_else(|_| spf.path.to_path_buf());
|
||||
if seen_paths.contains(&path) {
|
||||
continue;
|
||||
};
|
||||
seen_paths.insert(path.clone());
|
||||
seen_paths.insert(path);
|
||||
}
|
||||
// Use the original path (potentially with unresolved symlinks),
|
||||
// filesystem code should not care, but this is nicer for diagnostics.
|
||||
let path = spf.path.to_path_buf();
|
||||
match kind {
|
||||
CrateFlavor::Rlib => rlibs.insert(path, search_path.kind),
|
||||
CrateFlavor::Rmeta => rmetas.insert(path, search_path.kind),
|
||||
|
|
|
@ -564,13 +564,17 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
ExprKind::InlineAsm(box InlineAsmExpr {
|
||||
asm_macro: AsmMacro::Asm | AsmMacro::NakedAsm,
|
||||
asm_macro: asm_macro @ (AsmMacro::Asm | AsmMacro::NakedAsm),
|
||||
ref operands,
|
||||
template: _,
|
||||
options: _,
|
||||
line_spans: _,
|
||||
}) => {
|
||||
// The `naked` attribute and the `naked_asm!` block form one atomic unit of
|
||||
// unsafety, and `naked_asm!` does not itself need to be wrapped in an unsafe block.
|
||||
if let AsmMacro::Asm = asm_macro {
|
||||
self.requires_unsafe(expr.span, UseOfInlineAssembly);
|
||||
}
|
||||
|
||||
// For inline asm, do not use `walk_expr`, since we want to handle the label block
|
||||
// specially.
|
||||
|
|
|
@ -288,6 +288,21 @@ where
|
|||
) -> Vec<Candidate<I>>;
|
||||
}
|
||||
|
||||
/// Allows callers of `assemble_and_evaluate_candidates` to choose whether to limit
|
||||
/// candidate assembly to param-env and alias-bound candidates.
|
||||
///
|
||||
/// On top of being a micro-optimization, as it avoids doing unnecessary work when
|
||||
/// a param-env trait bound candidate shadows impls for normalization, this is also
|
||||
/// required to prevent query cycles due to RPITIT inference. See the issue at:
|
||||
/// <https://github.com/rust-lang/trait-system-refactor-initiative/issues/173>.
|
||||
pub(super) enum AssembleCandidatesFrom {
|
||||
All,
|
||||
/// Only assemble candidates from the environment and alias bounds, ignoring
|
||||
/// user-written and built-in impls. We only expect `ParamEnv` and `AliasBound`
|
||||
/// candidates to be assembled.
|
||||
EnvAndBounds,
|
||||
}
|
||||
|
||||
impl<D, I> EvalCtxt<'_, D>
|
||||
where
|
||||
D: SolverDelegate<Interner = I>,
|
||||
|
@ -296,6 +311,7 @@ where
|
|||
pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<D>>(
|
||||
&mut self,
|
||||
goal: Goal<I, G>,
|
||||
assemble_from: AssembleCandidatesFrom,
|
||||
) -> Vec<Candidate<I>> {
|
||||
let Ok(normalized_self_ty) =
|
||||
self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
|
||||
|
@ -322,16 +338,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
self.assemble_impl_candidates(goal, &mut candidates);
|
||||
|
||||
self.assemble_builtin_impl_candidates(goal, &mut candidates);
|
||||
|
||||
self.assemble_alias_bound_candidates(goal, &mut candidates);
|
||||
|
||||
self.assemble_object_bound_candidates(goal, &mut candidates);
|
||||
|
||||
self.assemble_param_env_candidates(goal, &mut candidates);
|
||||
|
||||
match assemble_from {
|
||||
AssembleCandidatesFrom::All => {
|
||||
self.assemble_impl_candidates(goal, &mut candidates);
|
||||
self.assemble_builtin_impl_candidates(goal, &mut candidates);
|
||||
self.assemble_object_bound_candidates(goal, &mut candidates);
|
||||
}
|
||||
AssembleCandidatesFrom::EnvAndBounds => {}
|
||||
}
|
||||
|
||||
candidates
|
||||
}
|
||||
|
||||
|
@ -754,6 +772,9 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
/// Assemble and merge candidates for goals which are related to an underlying trait
|
||||
/// goal. Right now, this is normalizes-to and host effect goals.
|
||||
///
|
||||
/// We sadly can't simply take all possible candidates for normalization goals
|
||||
/// and check whether they result in the same constraints. We want to make sure
|
||||
/// that trying to normalize an alias doesn't result in constraints which aren't
|
||||
|
@ -782,47 +803,44 @@ where
|
|||
///
|
||||
/// See trait-system-refactor-initiative#124 for more details.
|
||||
#[instrument(level = "debug", skip(self, inject_normalize_to_rigid_candidate), ret)]
|
||||
pub(super) fn merge_candidates(
|
||||
pub(super) fn assemble_and_merge_candidates<G: GoalKind<D>>(
|
||||
&mut self,
|
||||
proven_via: Option<TraitGoalProvenVia>,
|
||||
candidates: Vec<Candidate<I>>,
|
||||
goal: Goal<I, G>,
|
||||
inject_normalize_to_rigid_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
|
||||
) -> QueryResult<I> {
|
||||
let Some(proven_via) = proven_via else {
|
||||
// We don't care about overflow. If proving the trait goal overflowed, then
|
||||
// it's enough to report an overflow error for that, we don't also have to
|
||||
// overflow during normalization.
|
||||
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Ambiguity));
|
||||
//
|
||||
// We use `forced_ambiguity` here over `make_ambiguous_response_no_constraints`
|
||||
// because the former will also record a built-in candidate in the inspector.
|
||||
return self.forced_ambiguity(MaybeCause::Ambiguity).map(|cand| cand.result);
|
||||
};
|
||||
|
||||
match proven_via {
|
||||
TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => {
|
||||
let mut considered_candidates = Vec::new();
|
||||
considered_candidates.extend(
|
||||
candidates
|
||||
.iter()
|
||||
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
|
||||
.map(|c| c.result),
|
||||
);
|
||||
|
||||
// Even when a trait bound has been proven using a where-bound, we
|
||||
// still need to consider alias-bounds for normalization, see
|
||||
// tests/ui/next-solver/alias-bound-shadowed-by-env.rs.
|
||||
//
|
||||
// `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`.
|
||||
let candidates_from_env_and_bounds: Vec<_> = self
|
||||
.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds);
|
||||
|
||||
// We still need to prefer where-bounds over alias-bounds however.
|
||||
// See tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs.
|
||||
//
|
||||
// FIXME(const_trait_impl): should this behavior also be used by
|
||||
// constness checking. Doing so is *at least theoretically* breaking,
|
||||
// see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754
|
||||
if considered_candidates.is_empty() {
|
||||
considered_candidates.extend(
|
||||
candidates
|
||||
// See `tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs`.
|
||||
let mut considered_candidates: Vec<_> = if candidates_from_env_and_bounds
|
||||
.iter()
|
||||
.filter(|c| matches!(c.source, CandidateSource::AliasBound))
|
||||
.map(|c| c.result),
|
||||
);
|
||||
}
|
||||
.any(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
|
||||
{
|
||||
candidates_from_env_and_bounds
|
||||
.into_iter()
|
||||
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
|
||||
.map(|c| c.result)
|
||||
.collect()
|
||||
} else {
|
||||
candidates_from_env_and_bounds.into_iter().map(|c| c.result).collect()
|
||||
};
|
||||
|
||||
// If the trait goal has been proven by using the environment, we want to treat
|
||||
// aliases as rigid if there are no applicable projection bounds in the environment.
|
||||
|
@ -839,6 +857,9 @@ where
|
|||
}
|
||||
}
|
||||
TraitGoalProvenVia::Misc => {
|
||||
let candidates =
|
||||
self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
|
||||
|
||||
// Prefer "orphaned" param-env normalization predicates, which are used
|
||||
// (for example, and ideally only) when proving item bounds for an impl.
|
||||
let candidates_from_env: Vec<_> = candidates
|
||||
|
|
|
@ -399,12 +399,11 @@ where
|
|||
&mut self,
|
||||
goal: Goal<I, ty::HostEffectPredicate<I>>,
|
||||
) -> QueryResult<I> {
|
||||
let candidates = self.assemble_and_evaluate_candidates(goal);
|
||||
let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
|
||||
let trait_goal: Goal<I, ty::TraitPredicate<I>> =
|
||||
goal.with(ecx.cx(), goal.predicate.trait_ref);
|
||||
ecx.compute_trait_goal(trait_goal)
|
||||
})?;
|
||||
self.merge_candidates(proven_via, candidates, |_ecx| Err(NoSolution))
|
||||
self.assemble_and_merge_candidates(proven_via, goal, |_ecx| Err(NoSolution))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,14 +32,13 @@ where
|
|||
let cx = self.cx();
|
||||
match goal.predicate.alias.kind(cx) {
|
||||
ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
|
||||
let candidates = self.assemble_and_evaluate_candidates(goal);
|
||||
let trait_ref = goal.predicate.alias.trait_ref(cx);
|
||||
let (_, proven_via) =
|
||||
self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
|
||||
let trait_goal: Goal<I, ty::TraitPredicate<I>> = goal.with(cx, trait_ref);
|
||||
ecx.compute_trait_goal(trait_goal)
|
||||
})?;
|
||||
self.merge_candidates(proven_via, candidates, |ecx| {
|
||||
self.assemble_and_merge_candidates(proven_via, goal, |ecx| {
|
||||
ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| {
|
||||
this.structurally_instantiate_normalizes_to_term(
|
||||
goal,
|
||||
|
|
|
@ -13,7 +13,7 @@ use tracing::{instrument, trace};
|
|||
|
||||
use crate::delegate::SolverDelegate;
|
||||
use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
|
||||
use crate::solve::assembly::{self, Candidate};
|
||||
use crate::solve::assembly::{self, AssembleCandidatesFrom, Candidate};
|
||||
use crate::solve::inspect::ProbeKind;
|
||||
use crate::solve::{
|
||||
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
|
||||
|
@ -1365,7 +1365,7 @@ where
|
|||
&mut self,
|
||||
goal: Goal<I, TraitPredicate<I>>,
|
||||
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
|
||||
let candidates = self.assemble_and_evaluate_candidates(goal);
|
||||
let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
|
||||
self.merge_trait_candidates(goal, candidates)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -194,12 +194,6 @@ pub fn check_attribute_safety(psess: &ParseSess, safety: AttributeSafety, attr:
|
|||
}
|
||||
}
|
||||
} else if let Safety::Unsafe(unsafe_span) = attr_item.unsafety {
|
||||
// Allow (but don't require) `#[unsafe(naked)]` so that compiler-builtins can upgrade to it.
|
||||
// FIXME(#139797): remove this special case when compiler-builtins has upgraded.
|
||||
if attr.has_name(sym::naked) {
|
||||
return;
|
||||
}
|
||||
|
||||
psess.dcx().emit_err(errors::InvalidAttrUnsafe {
|
||||
span: unsafe_span,
|
||||
name: attr_item.path.clone(),
|
||||
|
|
|
@ -508,7 +508,7 @@ passes_must_use_no_effect =
|
|||
`#[must_use]` has no effect when applied to {$article} {$target}
|
||||
|
||||
passes_naked_asm_outside_naked_fn =
|
||||
the `naked_asm!` macro can only be used in functions marked with `#[naked]`
|
||||
the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]`
|
||||
|
||||
passes_naked_functions_asm_block =
|
||||
naked functions must contain a single `naked_asm!` invocation
|
||||
|
@ -516,9 +516,9 @@ passes_naked_functions_asm_block =
|
|||
.label_non_asm = not allowed in naked functions
|
||||
|
||||
passes_naked_functions_incompatible_attribute =
|
||||
attribute incompatible with `#[naked]`
|
||||
.label = the `{$attr}` attribute is incompatible with `#[naked]`
|
||||
.naked_attribute = function marked with `#[naked]` here
|
||||
attribute incompatible with `#[unsafe(naked)]`
|
||||
.label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]`
|
||||
.naked_attribute = function marked with `#[unsafe(naked)]` here
|
||||
|
||||
passes_naked_functions_must_naked_asm =
|
||||
the `asm!` macro is not allowed in naked functions
|
||||
|
|
|
@ -1232,6 +1232,25 @@ impl DesugaringKind {
|
|||
DesugaringKind::PatTyRange => "pattern type",
|
||||
}
|
||||
}
|
||||
|
||||
/// For use with `rustc_unimplemented` to support conditions
|
||||
/// like `from_desugaring = "QuestionMark"`
|
||||
pub fn matches(&self, value: &str) -> bool {
|
||||
match self {
|
||||
DesugaringKind::CondTemporary => value == "CondTemporary",
|
||||
DesugaringKind::Async => value == "Async",
|
||||
DesugaringKind::Await => value == "Await",
|
||||
DesugaringKind::QuestionMark => value == "QuestionMark",
|
||||
DesugaringKind::TryBlock => value == "TryBlock",
|
||||
DesugaringKind::YeetExpr => value == "YeetExpr",
|
||||
DesugaringKind::OpaqueTy => value == "OpaqueTy",
|
||||
DesugaringKind::ForLoop => value == "ForLoop",
|
||||
DesugaringKind::WhileLoop => value == "WhileLoop",
|
||||
DesugaringKind::BoundModifier => value == "BoundModifier",
|
||||
DesugaringKind::Contract => value == "Contract",
|
||||
DesugaringKind::PatTyRange => value == "PatTyRange",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
|
@ -372,6 +372,7 @@ symbols! {
|
|||
SyncUnsafeCell,
|
||||
T,
|
||||
Target,
|
||||
This,
|
||||
ToOwned,
|
||||
ToString,
|
||||
TokenStream,
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use crate::spec::{LinkSelfContainedDefault, TargetOptions, base, crt_objects};
|
||||
|
||||
pub(crate) fn opts() -> TargetOptions {
|
||||
let mut base = base::linux::opts();
|
||||
|
||||
base.env = "musl".into();
|
||||
base.pre_link_objects_self_contained = crt_objects::pre_musl_self_contained();
|
||||
base.post_link_objects_self_contained = crt_objects::post_musl_self_contained();
|
||||
base.link_self_contained = LinkSelfContainedDefault::InferredForMusl;
|
||||
|
||||
base
|
||||
TargetOptions {
|
||||
env: "musl".into(),
|
||||
pre_link_objects_self_contained: crt_objects::pre_musl_self_contained(),
|
||||
post_link_objects_self_contained: crt_objects::post_musl_self_contained(),
|
||||
link_self_contained: LinkSelfContainedDefault::InferredForMusl,
|
||||
..base::linux::opts()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use crate::spec::{TargetOptions, TlsModel, base};
|
||||
|
||||
pub(crate) fn opts() -> TargetOptions {
|
||||
let mut base = base::linux::opts();
|
||||
|
||||
base.env = "ohos".into();
|
||||
base.crt_static_default = false;
|
||||
base.tls_model = TlsModel::Emulated;
|
||||
base.has_thread_local = false;
|
||||
|
||||
base
|
||||
TargetOptions {
|
||||
env: "ohos".into(),
|
||||
crt_static_default: false,
|
||||
tls_model: TlsModel::Emulated,
|
||||
has_thread_local: false,
|
||||
..base::linux::opts()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ pub mod ambiguity;
|
|||
pub mod call_kind;
|
||||
mod fulfillment_errors;
|
||||
pub mod on_unimplemented;
|
||||
pub mod on_unimplemented_condition;
|
||||
pub mod on_unimplemented_format;
|
||||
mod overflow;
|
||||
pub mod suggestions;
|
||||
|
||||
|
|
|
@ -1,44 +1,31 @@
|
|||
use std::iter;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use rustc_ast::MetaItemInner;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit};
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{AttrArgs, Attribute};
|
||||
use rustc_macros::LintDiagnostic;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
||||
use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt};
|
||||
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
|
||||
use rustc_middle::ty::print::PrintTraitRefExt;
|
||||
use rustc_middle::ty::{self, GenericArgsRef, GenericParamDef, GenericParamDefKind, TyCtxt};
|
||||
use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
|
||||
use rustc_span::{Ident, Span, Symbol, kw, sym};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use tracing::{debug, info};
|
||||
use {rustc_attr_parsing as attr, rustc_hir as hir};
|
||||
|
||||
use super::{ObligationCauseCode, PredicateObligation};
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
use crate::error_reporting::traits::on_unimplemented_condition::{Condition, ConditionOptions};
|
||||
use crate::error_reporting::traits::on_unimplemented_format::{
|
||||
Ctx, FormatArgs, FormatString, FormatWarning,
|
||||
};
|
||||
use crate::errors::{
|
||||
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
|
||||
};
|
||||
use crate::infer::InferCtxtExt;
|
||||
|
||||
/// The symbols which are always allowed in a format string
|
||||
static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
|
||||
kw::SelfUpper,
|
||||
sym::ItemContext,
|
||||
sym::from_desugaring,
|
||||
sym::direct,
|
||||
sym::cause,
|
||||
sym::integral,
|
||||
sym::integer_,
|
||||
sym::float,
|
||||
sym::_Self,
|
||||
sym::crate_local,
|
||||
sym::Trait,
|
||||
];
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
fn impl_similar_to(
|
||||
&self,
|
||||
|
@ -121,86 +108,78 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
.unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args));
|
||||
let trait_pred = trait_pred.skip_binder();
|
||||
|
||||
let mut flags = vec![];
|
||||
let mut self_types = vec![];
|
||||
let mut generic_args: Vec<(Symbol, String)> = vec![];
|
||||
let mut crate_local = false;
|
||||
// FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs,
|
||||
// but I guess we could synthesize one here. We don't see any errors that rely on
|
||||
// that yet, though.
|
||||
let enclosure = self.describe_enclosure(obligation.cause.body_id).map(|t| t.to_owned());
|
||||
flags.push((sym::ItemContext, enclosure));
|
||||
let item_context = self.describe_enclosure(obligation.cause.body_id).unwrap_or("");
|
||||
|
||||
match obligation.cause.code() {
|
||||
let direct = match obligation.cause.code() {
|
||||
ObligationCauseCode::BuiltinDerived(..)
|
||||
| ObligationCauseCode::ImplDerived(..)
|
||||
| ObligationCauseCode::WellFormedDerived(..) => {}
|
||||
| ObligationCauseCode::WellFormedDerived(..) => false,
|
||||
_ => {
|
||||
// this is a "direct", user-specified, rather than derived,
|
||||
// obligation.
|
||||
flags.push((sym::direct, None));
|
||||
}
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(k) = obligation.cause.span.desugaring_kind() {
|
||||
flags.push((sym::from_desugaring, None));
|
||||
flags.push((sym::from_desugaring, Some(format!("{k:?}"))));
|
||||
}
|
||||
let from_desugaring = obligation.cause.span.desugaring_kind();
|
||||
|
||||
if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
|
||||
flags.push((sym::cause, Some("MainFunctionType".to_string())));
|
||||
}
|
||||
|
||||
flags.push((sym::Trait, Some(trait_pred.trait_ref.print_trait_sugared().to_string())));
|
||||
let cause = if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
|
||||
Some("MainFunctionType".to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Add all types without trimmed paths or visible paths, ensuring they end up with
|
||||
// their "canonical" def path.
|
||||
ty::print::with_no_trimmed_paths!(ty::print::with_no_visible_paths!({
|
||||
let generics = self.tcx.generics_of(def_id);
|
||||
let self_ty = trait_pred.self_ty();
|
||||
// This is also included through the generics list as `Self`,
|
||||
// but the parser won't allow you to use it
|
||||
flags.push((sym::_Self, Some(self_ty.to_string())));
|
||||
self_types.push(self_ty.to_string());
|
||||
if let Some(def) = self_ty.ty_adt_def() {
|
||||
// We also want to be able to select self's original
|
||||
// signature with no type arguments resolved
|
||||
flags.push((
|
||||
sym::_Self,
|
||||
Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
|
||||
));
|
||||
self_types.push(self.tcx.type_of(def.did()).instantiate_identity().to_string());
|
||||
}
|
||||
|
||||
for param in generics.own_params.iter() {
|
||||
let value = match param.kind {
|
||||
for GenericParamDef { name, kind, index, .. } in generics.own_params.iter() {
|
||||
let value = match kind {
|
||||
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
|
||||
args[param.index as usize].to_string()
|
||||
args[*index as usize].to_string()
|
||||
}
|
||||
GenericParamDefKind::Lifetime => continue,
|
||||
};
|
||||
let name = param.name;
|
||||
flags.push((name, Some(value)));
|
||||
generic_args.push((*name, value));
|
||||
|
||||
if let GenericParamDefKind::Type { .. } = param.kind {
|
||||
let param_ty = args[param.index as usize].expect_ty();
|
||||
if let GenericParamDefKind::Type { .. } = kind {
|
||||
let param_ty = args[*index as usize].expect_ty();
|
||||
if let Some(def) = param_ty.ty_adt_def() {
|
||||
// We also want to be able to select the parameter's
|
||||
// original signature with no type arguments resolved
|
||||
flags.push((
|
||||
name,
|
||||
Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
|
||||
generic_args.push((
|
||||
*name,
|
||||
self.tcx.type_of(def.did()).instantiate_identity().to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) {
|
||||
flags.push((sym::crate_local, None));
|
||||
crate_local = true;
|
||||
}
|
||||
|
||||
// Allow targeting all integers using `{integral}`, even if the exact type was resolved
|
||||
if self_ty.is_integral() {
|
||||
flags.push((sym::_Self, Some("{integral}".to_owned())));
|
||||
self_types.push("{integral}".to_owned());
|
||||
}
|
||||
|
||||
if self_ty.is_array_slice() {
|
||||
flags.push((sym::_Self, Some("&[]".to_owned())));
|
||||
self_types.push("&[]".to_owned());
|
||||
}
|
||||
|
||||
if self_ty.is_fn() {
|
||||
|
@ -215,53 +194,51 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
hir::Safety::Unsafe => "unsafe fn",
|
||||
}
|
||||
};
|
||||
flags.push((sym::_Self, Some(shortname.to_owned())));
|
||||
self_types.push(shortname.to_owned());
|
||||
}
|
||||
|
||||
// Slices give us `[]`, `[{ty}]`
|
||||
if let ty::Slice(aty) = self_ty.kind() {
|
||||
flags.push((sym::_Self, Some("[]".to_string())));
|
||||
self_types.push("[]".to_owned());
|
||||
if let Some(def) = aty.ty_adt_def() {
|
||||
// We also want to be able to select the slice's type's original
|
||||
// signature with no type arguments resolved
|
||||
flags.push((
|
||||
sym::_Self,
|
||||
Some(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity())),
|
||||
));
|
||||
self_types
|
||||
.push(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity()));
|
||||
}
|
||||
if aty.is_integral() {
|
||||
flags.push((sym::_Self, Some("[{integral}]".to_string())));
|
||||
self_types.push("[{integral}]".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
|
||||
if let ty::Array(aty, len) = self_ty.kind() {
|
||||
flags.push((sym::_Self, Some("[]".to_string())));
|
||||
self_types.push("[]".to_string());
|
||||
let len = len.try_to_target_usize(self.tcx);
|
||||
flags.push((sym::_Self, Some(format!("[{aty}; _]"))));
|
||||
self_types.push(format!("[{aty}; _]"));
|
||||
if let Some(n) = len {
|
||||
flags.push((sym::_Self, Some(format!("[{aty}; {n}]"))));
|
||||
self_types.push(format!("[{aty}; {n}]"));
|
||||
}
|
||||
if let Some(def) = aty.ty_adt_def() {
|
||||
// We also want to be able to select the array's type's original
|
||||
// signature with no type arguments resolved
|
||||
let def_ty = self.tcx.type_of(def.did()).instantiate_identity();
|
||||
flags.push((sym::_Self, Some(format!("[{def_ty}; _]"))));
|
||||
self_types.push(format!("[{def_ty}; _]"));
|
||||
if let Some(n) = len {
|
||||
flags.push((sym::_Self, Some(format!("[{def_ty}; {n}]"))));
|
||||
self_types.push(format!("[{def_ty}; {n}]"));
|
||||
}
|
||||
}
|
||||
if aty.is_integral() {
|
||||
flags.push((sym::_Self, Some("[{integral}; _]".to_string())));
|
||||
self_types.push("[{integral}; _]".to_string());
|
||||
if let Some(n) = len {
|
||||
flags.push((sym::_Self, Some(format!("[{{integral}}; {n}]"))));
|
||||
self_types.push(format!("[{{integral}}; {n}]"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let ty::Dynamic(traits, _, _) = self_ty.kind() {
|
||||
for t in traits.iter() {
|
||||
if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
|
||||
flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
|
||||
self_types.push(self.tcx.def_path_str(trait_ref.def_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -271,31 +248,76 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
&& let ty::Slice(sty) = ref_ty.kind()
|
||||
&& sty.is_integral()
|
||||
{
|
||||
flags.push((sym::_Self, Some("&[{integral}]".to_owned())));
|
||||
self_types.push("&[{integral}]".to_owned());
|
||||
}
|
||||
}));
|
||||
|
||||
let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id);
|
||||
let trait_sugared = trait_pred.trait_ref.print_trait_sugared();
|
||||
|
||||
let condition_options = ConditionOptions {
|
||||
self_types,
|
||||
from_desugaring,
|
||||
cause,
|
||||
crate_local,
|
||||
direct,
|
||||
generic_args,
|
||||
};
|
||||
|
||||
// Unlike the generic_args earlier,
|
||||
// this one is *not* collected under `with_no_trimmed_paths!`
|
||||
// for printing the type to the user
|
||||
//
|
||||
// This includes `Self`, as it is the first parameter in `own_params`.
|
||||
let generic_args = self
|
||||
.tcx
|
||||
.generics_of(trait_pred.trait_ref.def_id)
|
||||
.own_params
|
||||
.iter()
|
||||
.filter_map(|param| {
|
||||
let value = match param.kind {
|
||||
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
|
||||
if let Some(ty) = trait_pred.trait_ref.args[param.index as usize].as_type()
|
||||
{
|
||||
self.tcx.short_string(ty, long_ty_file)
|
||||
} else {
|
||||
trait_pred.trait_ref.args[param.index as usize].to_string()
|
||||
}
|
||||
}
|
||||
GenericParamDefKind::Lifetime => return None,
|
||||
};
|
||||
let name = param.name;
|
||||
Some((name, value))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let format_args = FormatArgs { this, trait_sugared, generic_args, item_context };
|
||||
|
||||
if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) {
|
||||
command.evaluate(self.tcx, trait_pred.trait_ref, &flags, long_ty_file)
|
||||
command.evaluate(self.tcx, trait_pred.trait_ref, &condition_options, &format_args)
|
||||
} else {
|
||||
OnUnimplementedNote::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a format string in a on_unimplemented attribute,
|
||||
/// like the "content" in `#[diagnostic::on_unimplemented(message = "content")]`
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OnUnimplementedFormatString {
|
||||
symbol: Symbol,
|
||||
span: Span,
|
||||
is_diagnostic_namespace_variant: bool,
|
||||
/// Symbol of the format string, i.e. `"content"`
|
||||
pub symbol: Symbol,
|
||||
///The span of the format string, i.e. `"content"`
|
||||
pub span: Span,
|
||||
pub is_diagnostic_namespace_variant: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OnUnimplementedDirective {
|
||||
pub condition: Option<MetaItemInner>,
|
||||
pub condition: Option<Condition>,
|
||||
pub subcommands: Vec<OnUnimplementedDirective>,
|
||||
pub message: Option<OnUnimplementedFormatString>,
|
||||
pub label: Option<OnUnimplementedFormatString>,
|
||||
pub message: Option<(Span, OnUnimplementedFormatString)>,
|
||||
pub label: Option<(Span, OnUnimplementedFormatString)>,
|
||||
pub notes: Vec<OnUnimplementedFormatString>,
|
||||
pub parent_label: Option<OnUnimplementedFormatString>,
|
||||
pub append_const_msg: Option<AppendConstMessage>,
|
||||
|
@ -329,7 +351,7 @@ pub struct MalformedOnUnimplementedAttrLint {
|
|||
}
|
||||
|
||||
impl MalformedOnUnimplementedAttrLint {
|
||||
fn new(span: Span) -> Self {
|
||||
pub fn new(span: Span) -> Self {
|
||||
Self { span }
|
||||
}
|
||||
}
|
||||
|
@ -350,7 +372,7 @@ pub struct IgnoredDiagnosticOption {
|
|||
}
|
||||
|
||||
impl IgnoredDiagnosticOption {
|
||||
fn maybe_emit_warning<'tcx>(
|
||||
pub fn maybe_emit_warning<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item_def_id: DefId,
|
||||
new: Option<Span>,
|
||||
|
@ -370,29 +392,11 @@ impl IgnoredDiagnosticOption {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)]
|
||||
#[help]
|
||||
pub struct UnknownFormatParameterForOnUnimplementedAttr {
|
||||
argument_name: Symbol,
|
||||
trait_name: Ident,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(trait_selection_disallowed_positional_argument)]
|
||||
#[help]
|
||||
pub struct DisallowedPositionalArgument;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(trait_selection_invalid_format_specifier)]
|
||||
#[help]
|
||||
pub struct InvalidFormatSpecifier;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(trait_selection_wrapped_parser_error)]
|
||||
pub struct WrappedParserError {
|
||||
description: String,
|
||||
label: String,
|
||||
pub description: String,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl<'tcx> OnUnimplementedDirective {
|
||||
|
@ -407,12 +411,12 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
let mut errored = None;
|
||||
let mut item_iter = items.iter();
|
||||
|
||||
let parse_value = |value_str, value_span| {
|
||||
let parse_value = |value_str, span| {
|
||||
OnUnimplementedFormatString::try_parse(
|
||||
tcx,
|
||||
item_def_id,
|
||||
value_str,
|
||||
value_span,
|
||||
span,
|
||||
is_diagnostic_namespace_variant,
|
||||
)
|
||||
.map(Some)
|
||||
|
@ -434,7 +438,7 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
}
|
||||
true
|
||||
});
|
||||
Some(cond.clone())
|
||||
Some(Condition { inner: cond.clone() })
|
||||
};
|
||||
|
||||
let mut message = None;
|
||||
|
@ -444,24 +448,36 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
let mut subcommands = vec![];
|
||||
let mut append_const_msg = None;
|
||||
|
||||
let get_value_and_span = |item: &_, key| {
|
||||
if let MetaItemInner::MetaItem(MetaItem {
|
||||
path,
|
||||
kind: MetaItemKind::NameValue(MetaItemLit { span, kind: LitKind::Str(s, _), .. }),
|
||||
..
|
||||
}) = item
|
||||
&& *path == key
|
||||
{
|
||||
Some((*s, *span))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
for item in item_iter {
|
||||
if item.has_name(sym::message) && message.is_none() {
|
||||
if let Some(message_) = item.value_str() {
|
||||
message = parse_value(message_, item.span())?;
|
||||
if let Some((message_, span)) = get_value_and_span(item, sym::message)
|
||||
&& message.is_none()
|
||||
{
|
||||
message = parse_value(message_, span)?.map(|l| (item.span(), l));
|
||||
continue;
|
||||
}
|
||||
} else if item.has_name(sym::label) && label.is_none() {
|
||||
if let Some(label_) = item.value_str() {
|
||||
label = parse_value(label_, item.span())?;
|
||||
} else if let Some((label_, span)) = get_value_and_span(item, sym::label)
|
||||
&& label.is_none()
|
||||
{
|
||||
label = parse_value(label_, span)?.map(|l| (item.span(), l));
|
||||
continue;
|
||||
}
|
||||
} else if item.has_name(sym::note) {
|
||||
if let Some(note_) = item.value_str() {
|
||||
if let Some(note) = parse_value(note_, item.span())? {
|
||||
} else if let Some((note_, span)) = get_value_and_span(item, sym::note) {
|
||||
if let Some(note) = parse_value(note_, span)? {
|
||||
notes.push(note);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if item.has_name(sym::parent_label)
|
||||
&& parent_label.is_none()
|
||||
&& !is_diagnostic_namespace_variant
|
||||
|
@ -539,6 +555,13 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
}
|
||||
|
||||
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
|
||||
if !tcx.is_trait(item_def_id) {
|
||||
// It could be a trait_alias (`trait MyTrait = SomeOtherTrait`)
|
||||
// or an implementation (`impl MyTrait for Foo {}`)
|
||||
//
|
||||
// We don't support those.
|
||||
return Ok(None);
|
||||
}
|
||||
if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
|
||||
return Self::parse_attribute(attr, false, tcx, item_def_id);
|
||||
} else {
|
||||
|
@ -554,15 +577,15 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||
tcx,
|
||||
item_def_id,
|
||||
directive.message.as_ref().map(|f| f.span),
|
||||
aggr.message.as_ref().map(|f| f.span),
|
||||
directive.message.as_ref().map(|f| f.0),
|
||||
aggr.message.as_ref().map(|f| f.0),
|
||||
"message",
|
||||
);
|
||||
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||
tcx,
|
||||
item_def_id,
|
||||
directive.label.as_ref().map(|f| f.span),
|
||||
aggr.label.as_ref().map(|f| f.span),
|
||||
directive.label.as_ref().map(|f| f.0),
|
||||
aggr.label.as_ref().map(|f| f.0),
|
||||
"label",
|
||||
);
|
||||
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||
|
@ -636,13 +659,16 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
condition: None,
|
||||
message: None,
|
||||
subcommands: vec![],
|
||||
label: Some(OnUnimplementedFormatString::try_parse(
|
||||
label: Some((
|
||||
attr.span(),
|
||||
OnUnimplementedFormatString::try_parse(
|
||||
tcx,
|
||||
item_def_id,
|
||||
value,
|
||||
attr.span(),
|
||||
attr.value_span().unwrap_or(attr.span()),
|
||||
is_diagnostic_namespace_variant,
|
||||
)?),
|
||||
)?,
|
||||
)),
|
||||
notes: Vec::new(),
|
||||
parent_label: None,
|
||||
append_const_msg: None,
|
||||
|
@ -702,43 +728,23 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
trait_ref: ty::TraitRef<'tcx>,
|
||||
options: &[(Symbol, Option<String>)],
|
||||
long_ty_file: &mut Option<PathBuf>,
|
||||
condition_options: &ConditionOptions,
|
||||
args: &FormatArgs<'tcx>,
|
||||
) -> OnUnimplementedNote {
|
||||
let mut message = None;
|
||||
let mut label = None;
|
||||
let mut notes = Vec::new();
|
||||
let mut parent_label = None;
|
||||
let mut append_const_msg = None;
|
||||
info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
|
||||
|
||||
let options_map: FxHashMap<Symbol, String> =
|
||||
options.iter().filter_map(|(k, v)| v.clone().map(|v| (*k, v))).collect();
|
||||
info!(
|
||||
"evaluate({:?}, trait_ref={:?}, options={:?}, args ={:?})",
|
||||
self, trait_ref, condition_options, args
|
||||
);
|
||||
|
||||
for command in self.subcommands.iter().chain(Some(self)).rev() {
|
||||
debug!(?command);
|
||||
if let Some(ref condition) = command.condition
|
||||
&& !attr::eval_condition(condition, &tcx.sess, Some(tcx.features()), &mut |cfg| {
|
||||
let value = cfg.value.map(|v| {
|
||||
// `with_no_visible_paths` is also used when generating the options,
|
||||
// so we need to match it here.
|
||||
ty::print::with_no_visible_paths!(
|
||||
OnUnimplementedFormatString {
|
||||
symbol: v,
|
||||
span: cfg.span,
|
||||
is_diagnostic_namespace_variant: false
|
||||
}
|
||||
.format(
|
||||
tcx,
|
||||
trait_ref,
|
||||
&options_map,
|
||||
long_ty_file
|
||||
)
|
||||
)
|
||||
});
|
||||
|
||||
options.contains(&(cfg.name, value))
|
||||
})
|
||||
&& !condition.matches_predicate(tcx, condition_options)
|
||||
{
|
||||
debug!("evaluate: skipping {:?} due to condition", command);
|
||||
continue;
|
||||
|
@ -762,14 +768,10 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
}
|
||||
|
||||
OnUnimplementedNote {
|
||||
label: label.map(|l| l.format(tcx, trait_ref, &options_map, long_ty_file)),
|
||||
message: message.map(|m| m.format(tcx, trait_ref, &options_map, long_ty_file)),
|
||||
notes: notes
|
||||
.into_iter()
|
||||
.map(|n| n.format(tcx, trait_ref, &options_map, long_ty_file))
|
||||
.collect(),
|
||||
parent_label: parent_label
|
||||
.map(|e_s| e_s.format(tcx, trait_ref, &options_map, long_ty_file)),
|
||||
label: label.map(|l| l.1.format(tcx, trait_ref, args)),
|
||||
message: message.map(|m| m.1.format(tcx, trait_ref, args)),
|
||||
notes: notes.into_iter().map(|n| n.format(tcx, trait_ref, args)).collect(),
|
||||
parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, args)),
|
||||
append_const_msg,
|
||||
}
|
||||
}
|
||||
|
@ -780,106 +782,81 @@ impl<'tcx> OnUnimplementedFormatString {
|
|||
tcx: TyCtxt<'tcx>,
|
||||
item_def_id: DefId,
|
||||
from: Symbol,
|
||||
value_span: Span,
|
||||
span: Span,
|
||||
is_diagnostic_namespace_variant: bool,
|
||||
) -> Result<Self, ErrorGuaranteed> {
|
||||
let result = OnUnimplementedFormatString {
|
||||
symbol: from,
|
||||
span: value_span,
|
||||
is_diagnostic_namespace_variant,
|
||||
};
|
||||
let result =
|
||||
OnUnimplementedFormatString { symbol: from, span, is_diagnostic_namespace_variant };
|
||||
result.verify(tcx, item_def_id)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> {
|
||||
let trait_def_id = if tcx.is_trait(item_def_id) {
|
||||
item_def_id
|
||||
} else {
|
||||
tcx.trait_id_of_impl(item_def_id)
|
||||
.expect("expected `on_unimplemented` to correspond to a trait")
|
||||
fn verify(&self, tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> {
|
||||
if !tcx.is_trait(trait_def_id) {
|
||||
return Ok(());
|
||||
};
|
||||
let trait_name = tcx.item_ident(trait_def_id);
|
||||
let generics = tcx.generics_of(item_def_id);
|
||||
let s = self.symbol.as_str();
|
||||
let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
|
||||
|
||||
let ctx = if self.is_diagnostic_namespace_variant {
|
||||
Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }
|
||||
} else {
|
||||
Ctx::RustcOnUnimplemented { tcx, trait_def_id }
|
||||
};
|
||||
|
||||
let mut result = Ok(());
|
||||
for token in &mut parser {
|
||||
match token {
|
||||
Piece::Lit(_) => (), // Normal string, no need to check it
|
||||
Piece::NextArgument(a) => {
|
||||
let format_spec = a.format;
|
||||
if self.is_diagnostic_namespace_variant
|
||||
&& (format_spec.ty_span.is_some()
|
||||
|| format_spec.width_span.is_some()
|
||||
|| format_spec.precision_span.is_some()
|
||||
|| format_spec.fill_span.is_some())
|
||||
{
|
||||
if let Some(item_def_id) = item_def_id.as_local() {
|
||||
tcx.emit_node_span_lint(
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
tcx.local_def_id_to_hir_id(item_def_id),
|
||||
self.span,
|
||||
InvalidFormatSpecifier,
|
||||
);
|
||||
}
|
||||
}
|
||||
match a.position {
|
||||
Position::ArgumentNamed(s) => {
|
||||
match Symbol::intern(s) {
|
||||
// `{ThisTraitsName}` is allowed
|
||||
s if s == trait_name.name
|
||||
&& !self.is_diagnostic_namespace_variant =>
|
||||
{
|
||||
()
|
||||
}
|
||||
s if ALLOWED_FORMAT_SYMBOLS.contains(&s)
|
||||
&& !self.is_diagnostic_namespace_variant =>
|
||||
{
|
||||
()
|
||||
}
|
||||
// So is `{A}` if A is a type parameter
|
||||
s if generics.own_params.iter().any(|param| param.name == s) => (),
|
||||
s => {
|
||||
|
||||
match FormatString::parse(self.symbol, self.span, &ctx) {
|
||||
// Warnings about format specifiers, deprecated parameters, wrong parameters etc.
|
||||
// In other words we'd like to let the author know, but we can still try to format the string later
|
||||
Ok(FormatString { warnings, .. }) => {
|
||||
if self.is_diagnostic_namespace_variant {
|
||||
if let Some(item_def_id) = item_def_id.as_local() {
|
||||
tcx.emit_node_span_lint(
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
tcx.local_def_id_to_hir_id(item_def_id),
|
||||
self.span,
|
||||
UnknownFormatParameterForOnUnimplementedAttr {
|
||||
argument_name: s,
|
||||
trait_name,
|
||||
},
|
||||
);
|
||||
for w in warnings {
|
||||
w.emit_warning(tcx, trait_def_id)
|
||||
}
|
||||
} else {
|
||||
result = Err(struct_span_code_err!(
|
||||
for w in warnings {
|
||||
match w {
|
||||
FormatWarning::UnknownParam { argument_name, span } => {
|
||||
let reported = struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
self.span,
|
||||
span,
|
||||
E0230,
|
||||
"there is no parameter `{}` on {}",
|
||||
s,
|
||||
if trait_def_id == item_def_id {
|
||||
format!("trait `{trait_name}`")
|
||||
} else {
|
||||
"impl".to_string()
|
||||
}
|
||||
"cannot find parameter {} on this trait",
|
||||
argument_name,
|
||||
)
|
||||
.emit());
|
||||
.emit();
|
||||
result = Err(reported);
|
||||
}
|
||||
FormatWarning::PositionalArgument { span, .. } => {
|
||||
let reported = struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
span,
|
||||
E0231,
|
||||
"positional format arguments are not allowed here"
|
||||
)
|
||||
.emit();
|
||||
result = Err(reported);
|
||||
}
|
||||
FormatWarning::InvalidSpecifier { .. }
|
||||
| FormatWarning::FutureIncompat { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// `{:1}` and `{}` are not to be used
|
||||
Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
|
||||
// Errors from the underlying `rustc_parse_format::Parser`
|
||||
Err(errors) => {
|
||||
// we cannot return errors from processing the format string as hard error here
|
||||
// as the diagnostic namespace guarantees that malformed input cannot cause an error
|
||||
//
|
||||
// if we encounter any error while processing we nevertheless want to show it as warning
|
||||
// so that users are aware that something is not correct
|
||||
for e in errors {
|
||||
if self.is_diagnostic_namespace_variant {
|
||||
if let Some(item_def_id) = item_def_id.as_local() {
|
||||
if let Some(trait_def_id) = trait_def_id.as_local() {
|
||||
tcx.emit_node_span_lint(
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
tcx.local_def_id_to_hir_id(item_def_id),
|
||||
tcx.local_def_id_to_hir_id(trait_def_id),
|
||||
self.span,
|
||||
DisallowedPositionalArgument,
|
||||
WrappedParserError { description: e.description, label: e.label },
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
@ -887,7 +864,8 @@ impl<'tcx> OnUnimplementedFormatString {
|
|||
tcx.dcx(),
|
||||
self.span,
|
||||
E0231,
|
||||
"only named generic parameters are allowed"
|
||||
"{}",
|
||||
e.description,
|
||||
)
|
||||
.emit();
|
||||
result = Err(reported);
|
||||
|
@ -895,29 +873,6 @@ impl<'tcx> OnUnimplementedFormatString {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// we cannot return errors from processing the format string as hard error here
|
||||
// as the diagnostic namespace guarantees that malformed input cannot cause an error
|
||||
//
|
||||
// if we encounter any error while processing we nevertheless want to show it as warning
|
||||
// so that users are aware that something is not correct
|
||||
for e in parser.errors {
|
||||
if self.is_diagnostic_namespace_variant {
|
||||
if let Some(item_def_id) = item_def_id.as_local() {
|
||||
tcx.emit_node_span_lint(
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
tcx.local_def_id_to_hir_id(item_def_id),
|
||||
self.span,
|
||||
WrappedParserError { description: e.description, label: e.label },
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let reported =
|
||||
struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,).emit();
|
||||
result = Err(reported);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
@ -926,85 +881,18 @@ impl<'tcx> OnUnimplementedFormatString {
|
|||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
trait_ref: ty::TraitRef<'tcx>,
|
||||
options: &FxHashMap<Symbol, String>,
|
||||
long_ty_file: &mut Option<PathBuf>,
|
||||
args: &FormatArgs<'tcx>,
|
||||
) -> String {
|
||||
let name = tcx.item_name(trait_ref.def_id);
|
||||
let trait_str = tcx.def_path_str(trait_ref.def_id);
|
||||
let generics = tcx.generics_of(trait_ref.def_id);
|
||||
let generic_map = generics
|
||||
.own_params
|
||||
.iter()
|
||||
.filter_map(|param| {
|
||||
let value = match param.kind {
|
||||
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
|
||||
if let Some(ty) = trait_ref.args[param.index as usize].as_type() {
|
||||
tcx.short_string(ty, long_ty_file)
|
||||
let trait_def_id = trait_ref.def_id;
|
||||
let ctx = if self.is_diagnostic_namespace_variant {
|
||||
Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }
|
||||
} else {
|
||||
trait_ref.args[param.index as usize].to_string()
|
||||
}
|
||||
}
|
||||
GenericParamDefKind::Lifetime => return None,
|
||||
Ctx::RustcOnUnimplemented { tcx, trait_def_id }
|
||||
};
|
||||
let name = param.name;
|
||||
Some((name, value))
|
||||
})
|
||||
.collect::<FxHashMap<Symbol, String>>();
|
||||
let empty_string = String::new();
|
||||
|
||||
let s = self.symbol.as_str();
|
||||
let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
|
||||
let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
|
||||
let constructed_message = (&mut parser)
|
||||
.map(|p| match p {
|
||||
Piece::Lit(s) => s.to_owned(),
|
||||
Piece::NextArgument(a) => match a.position {
|
||||
Position::ArgumentNamed(arg) => {
|
||||
let s = Symbol::intern(arg);
|
||||
match generic_map.get(&s) {
|
||||
Some(val) => val.to_string(),
|
||||
None if self.is_diagnostic_namespace_variant => {
|
||||
format!("{{{arg}}}")
|
||||
}
|
||||
None if s == name => trait_str.clone(),
|
||||
None => {
|
||||
if let Some(val) = options.get(&s) {
|
||||
val.clone()
|
||||
} else if s == sym::from_desugaring {
|
||||
// don't break messages using these two arguments incorrectly
|
||||
String::new()
|
||||
} else if s == sym::ItemContext
|
||||
&& !self.is_diagnostic_namespace_variant
|
||||
{
|
||||
item_context.clone()
|
||||
} else if s == sym::integral {
|
||||
String::from("{integral}")
|
||||
} else if s == sym::integer_ {
|
||||
String::from("{integer}")
|
||||
} else if s == sym::float {
|
||||
String::from("{float}")
|
||||
if let Ok(s) = FormatString::parse(self.symbol, self.span, &ctx) {
|
||||
s.format(args)
|
||||
} else {
|
||||
bug!(
|
||||
"broken on_unimplemented {:?} for {:?}: \
|
||||
no argument matching {:?}",
|
||||
self.symbol,
|
||||
trait_ref,
|
||||
s
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Position::ArgumentImplicitlyIs(_) if self.is_diagnostic_namespace_variant => {
|
||||
String::from("{}")
|
||||
}
|
||||
Position::ArgumentIs(idx) if self.is_diagnostic_namespace_variant => {
|
||||
format!("{{{idx}}}")
|
||||
}
|
||||
_ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol),
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
// we cannot return errors from processing the format string as hard error here
|
||||
// as the diagnostic namespace guarantees that malformed input cannot cause an error
|
||||
//
|
||||
|
@ -1014,10 +902,7 @@ impl<'tcx> OnUnimplementedFormatString {
|
|||
//
|
||||
// The actual parser errors are emitted earlier
|
||||
// as lint warnings in OnUnimplementedFormatString::verify
|
||||
if self.is_diagnostic_namespace_variant && !parser.errors.is_empty() {
|
||||
String::from(s)
|
||||
} else {
|
||||
constructed_message
|
||||
self.symbol.as_str().into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
use rustc_ast::MetaItemInner;
|
||||
use rustc_attr_parsing as attr;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
|
||||
use rustc_span::{DesugaringKind, Span, Symbol, kw, sym};
|
||||
|
||||
/// A predicate in an attribute using on, all, any,
|
||||
/// similar to a cfg predicate.
|
||||
#[derive(Debug)]
|
||||
pub struct Condition {
|
||||
pub inner: MetaItemInner,
|
||||
}
|
||||
|
||||
impl Condition {
|
||||
pub fn span(&self) -> Span {
|
||||
self.inner.span()
|
||||
}
|
||||
|
||||
pub fn matches_predicate<'tcx>(&self, tcx: TyCtxt<'tcx>, options: &ConditionOptions) -> bool {
|
||||
attr::eval_condition(&self.inner, tcx.sess, Some(tcx.features()), &mut |cfg| {
|
||||
let value = cfg.value.map(|v| {
|
||||
// `with_no_visible_paths` is also used when generating the options,
|
||||
// so we need to match it here.
|
||||
ty::print::with_no_visible_paths!({
|
||||
Parser::new(v.as_str(), None, None, false, ParseMode::Format)
|
||||
.map(|p| match p {
|
||||
Piece::Lit(s) => s.to_owned(),
|
||||
Piece::NextArgument(a) => match a.position {
|
||||
Position::ArgumentNamed(arg) => {
|
||||
let s = Symbol::intern(arg);
|
||||
match options.generic_args.iter().find(|(k, _)| *k == s) {
|
||||
Some((_, val)) => val.to_string(),
|
||||
None => format!("{{{arg}}}"),
|
||||
}
|
||||
}
|
||||
Position::ArgumentImplicitlyIs(_) => String::from("{}"),
|
||||
Position::ArgumentIs(idx) => format!("{{{idx}}}"),
|
||||
},
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
});
|
||||
|
||||
options.contains(cfg.name, &value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Used with `Condition::matches_predicate` to test whether the condition applies
|
||||
///
|
||||
/// For example, given a
|
||||
/// ```rust,ignore (just an example)
|
||||
/// #[rustc_on_unimplemented(
|
||||
/// on(all(from_desugaring = "QuestionMark"),
|
||||
/// message = "the `?` operator can only be used in {ItemContext} \
|
||||
/// that returns `Result` or `Option` \
|
||||
/// (or another type that implements `{FromResidual}`)",
|
||||
/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`",
|
||||
/// parent_label = "this function should return `Result` or `Option` to accept `?`"
|
||||
/// ),
|
||||
/// )]
|
||||
/// pub trait FromResidual<R = <Self as Try>::Residual> {
|
||||
/// ...
|
||||
/// }
|
||||
///
|
||||
/// async fn an_async_function() -> u32 {
|
||||
/// let x: Option<u32> = None;
|
||||
/// x?; //~ ERROR the `?` operator
|
||||
/// 22
|
||||
/// }
|
||||
/// ```
|
||||
/// it will look like this:
|
||||
///
|
||||
/// ```rust,ignore (just an example)
|
||||
/// ConditionOptions {
|
||||
/// self_types: ["u32", "{integral}"],
|
||||
/// from_desugaring: Some("QuestionMark"),
|
||||
/// cause: None,
|
||||
/// crate_local: false,
|
||||
/// direct: true,
|
||||
/// generic_args: [("Self","u32"),
|
||||
/// ("R", "core::option::Option<core::convert::Infallible>"),
|
||||
/// ("R", "core::option::Option<T>" ),
|
||||
/// ],
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct ConditionOptions {
|
||||
/// All the self types that may apply.
|
||||
/// for example
|
||||
pub self_types: Vec<String>,
|
||||
// The kind of compiler desugaring.
|
||||
pub from_desugaring: Option<DesugaringKind>,
|
||||
/// Match on a variant of [rustc_infer::traits::ObligationCauseCode]
|
||||
pub cause: Option<String>,
|
||||
pub crate_local: bool,
|
||||
/// Is the obligation "directly" user-specified, rather than derived?
|
||||
pub direct: bool,
|
||||
// A list of the generic arguments and their reified types
|
||||
pub generic_args: Vec<(Symbol, String)>,
|
||||
}
|
||||
|
||||
impl ConditionOptions {
|
||||
pub fn contains(&self, key: Symbol, value: &Option<String>) -> bool {
|
||||
match (key, value) {
|
||||
(sym::_Self | kw::SelfUpper, Some(value)) => self.self_types.contains(&value),
|
||||
// from_desugaring as a flag
|
||||
(sym::from_desugaring, None) => self.from_desugaring.is_some(),
|
||||
// from_desugaring as key == value
|
||||
(sym::from_desugaring, Some(v)) if let Some(ds) = self.from_desugaring => ds.matches(v),
|
||||
(sym::cause, Some(value)) => self.cause.as_deref() == Some(value),
|
||||
(sym::crate_local, None) => self.crate_local,
|
||||
(sym::direct, None) => self.direct,
|
||||
(other, Some(value)) => {
|
||||
self.generic_args.iter().any(|(k, v)| *k == other && v == value)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,414 @@
|
|||
use std::fmt;
|
||||
|
||||
use errors::*;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_middle::ty::print::TraitRefPrintSugared;
|
||||
use rustc_parse_format::{
|
||||
Alignment, Argument, Count, FormatSpec, InnerSpan, ParseError, ParseMode, Parser,
|
||||
Piece as RpfPiece, Position,
|
||||
};
|
||||
use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::{BytePos, Pos, Span, Symbol, kw, sym};
|
||||
|
||||
/// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces",
|
||||
/// either as string pieces or dynamic arguments.
|
||||
#[derive(Debug)]
|
||||
pub struct FormatString {
|
||||
#[allow(dead_code, reason = "Debug impl")]
|
||||
input: Symbol,
|
||||
span: Span,
|
||||
pieces: Vec<Piece>,
|
||||
/// The formatting string was parsed succesfully but with warnings
|
||||
pub warnings: Vec<FormatWarning>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Piece {
|
||||
Lit(String),
|
||||
Arg(FormatArg),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum FormatArg {
|
||||
// A generic parameter, like `{T}` if we're on the `From<T>` trait.
|
||||
GenericParam {
|
||||
generic_param: Symbol,
|
||||
},
|
||||
// `{Self}`
|
||||
SelfUpper,
|
||||
/// `{This}` or `{TraitName}`
|
||||
This,
|
||||
/// The sugared form of the trait
|
||||
Trait,
|
||||
/// what we're in, like a function, method, closure etc.
|
||||
ItemContext,
|
||||
/// What the user typed, if it doesn't match anything we can use.
|
||||
AsIs(String),
|
||||
}
|
||||
|
||||
pub enum Ctx<'tcx> {
|
||||
// `#[rustc_on_unimplemented]`
|
||||
RustcOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId },
|
||||
// `#[diagnostic::...]`
|
||||
DiagnosticOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FormatWarning {
|
||||
UnknownParam { argument_name: Symbol, span: Span },
|
||||
PositionalArgument { span: Span, help: String },
|
||||
InvalidSpecifier { name: String, span: Span },
|
||||
FutureIncompat { span: Span, help: String },
|
||||
}
|
||||
|
||||
impl FormatWarning {
|
||||
pub fn emit_warning<'tcx>(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) {
|
||||
match *self {
|
||||
FormatWarning::UnknownParam { argument_name, span } => {
|
||||
let this = tcx.item_ident(item_def_id);
|
||||
if let Some(item_def_id) = item_def_id.as_local() {
|
||||
tcx.emit_node_span_lint(
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
tcx.local_def_id_to_hir_id(item_def_id),
|
||||
span,
|
||||
UnknownFormatParameterForOnUnimplementedAttr {
|
||||
argument_name,
|
||||
trait_name: this,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
FormatWarning::PositionalArgument { span, .. } => {
|
||||
if let Some(item_def_id) = item_def_id.as_local() {
|
||||
tcx.emit_node_span_lint(
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
tcx.local_def_id_to_hir_id(item_def_id),
|
||||
span,
|
||||
DisallowedPositionalArgument,
|
||||
);
|
||||
}
|
||||
}
|
||||
FormatWarning::InvalidSpecifier { span, .. } => {
|
||||
if let Some(item_def_id) = item_def_id.as_local() {
|
||||
tcx.emit_node_span_lint(
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
tcx.local_def_id_to_hir_id(item_def_id),
|
||||
span,
|
||||
InvalidFormatSpecifier,
|
||||
);
|
||||
}
|
||||
}
|
||||
FormatWarning::FutureIncompat { .. } => {
|
||||
// We've never deprecated anything in diagnostic namespace format strings
|
||||
// but if we do we will emit a warning here
|
||||
|
||||
// FIXME(mejrs) in a couple releases, start emitting warnings for
|
||||
// #[rustc_on_unimplemented] deprecated args
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Arguments to fill a [FormatString] with.
|
||||
///
|
||||
/// For example, given a
|
||||
/// ```rust,ignore (just an example)
|
||||
///
|
||||
/// #[rustc_on_unimplemented(
|
||||
/// on(all(from_desugaring = "QuestionMark"),
|
||||
/// message = "the `?` operator can only be used in {ItemContext} \
|
||||
/// that returns `Result` or `Option` \
|
||||
/// (or another type that implements `{FromResidual}`)",
|
||||
/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`",
|
||||
/// parent_label = "this function should return `Result` or `Option` to accept `?`"
|
||||
/// ),
|
||||
/// )]
|
||||
/// pub trait FromResidual<R = <Self as Try>::Residual> {
|
||||
/// ...
|
||||
/// }
|
||||
///
|
||||
/// async fn an_async_function() -> u32 {
|
||||
/// let x: Option<u32> = None;
|
||||
/// x?; //~ ERROR the `?` operator
|
||||
/// 22
|
||||
/// }
|
||||
/// ```
|
||||
/// it will look like this:
|
||||
///
|
||||
/// ```rust,ignore (just an example)
|
||||
/// FormatArgs {
|
||||
/// this: "FromResidual",
|
||||
/// trait_sugared: "FromResidual<Option<Infallible>>",
|
||||
/// item_context: "an async function",
|
||||
/// generic_args: [("Self", "u32"), ("R", "Option<Infallible>")],
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct FormatArgs<'tcx> {
|
||||
pub this: String,
|
||||
pub trait_sugared: TraitRefPrintSugared<'tcx>,
|
||||
pub item_context: &'static str,
|
||||
pub generic_args: Vec<(Symbol, String)>,
|
||||
}
|
||||
|
||||
impl FormatString {
|
||||
pub fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
pub fn parse<'tcx>(
|
||||
input: Symbol,
|
||||
span: Span,
|
||||
ctx: &Ctx<'tcx>,
|
||||
) -> Result<Self, Vec<ParseError>> {
|
||||
let s = input.as_str();
|
||||
let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
|
||||
let mut pieces = Vec::new();
|
||||
let mut warnings = Vec::new();
|
||||
|
||||
for piece in &mut parser {
|
||||
match piece {
|
||||
RpfPiece::Lit(lit) => {
|
||||
pieces.push(Piece::Lit(lit.into()));
|
||||
}
|
||||
RpfPiece::NextArgument(arg) => {
|
||||
warn_on_format_spec(arg.format, &mut warnings, span);
|
||||
let arg = parse_arg(&arg, ctx, &mut warnings, span);
|
||||
pieces.push(Piece::Arg(arg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if parser.errors.is_empty() {
|
||||
Ok(FormatString { input, pieces, span, warnings })
|
||||
} else {
|
||||
Err(parser.errors)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(&self, args: &FormatArgs<'_>) -> String {
|
||||
let mut ret = String::new();
|
||||
for piece in &self.pieces {
|
||||
match piece {
|
||||
Piece::Lit(s) | Piece::Arg(FormatArg::AsIs(s)) => ret.push_str(&s),
|
||||
|
||||
// `A` if we have `trait Trait<A> {}` and `note = "i'm the actual type of {A}"`
|
||||
Piece::Arg(FormatArg::GenericParam { generic_param }) => {
|
||||
// Should always be some but we can't raise errors here
|
||||
let value = match args.generic_args.iter().find(|(p, _)| p == generic_param) {
|
||||
Some((_, val)) => val.to_string(),
|
||||
None => generic_param.to_string(),
|
||||
};
|
||||
ret.push_str(&value);
|
||||
}
|
||||
// `{Self}`
|
||||
Piece::Arg(FormatArg::SelfUpper) => {
|
||||
let slf = match args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) {
|
||||
Some((_, val)) => val.to_string(),
|
||||
None => "Self".to_string(),
|
||||
};
|
||||
ret.push_str(&slf);
|
||||
}
|
||||
|
||||
// It's only `rustc_onunimplemented` from here
|
||||
Piece::Arg(FormatArg::This) => ret.push_str(&args.this),
|
||||
Piece::Arg(FormatArg::Trait) => {
|
||||
let _ = fmt::write(&mut ret, format_args!("{}", &args.trait_sugared));
|
||||
}
|
||||
Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context),
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_arg<'tcx>(
|
||||
arg: &Argument<'_>,
|
||||
ctx: &Ctx<'tcx>,
|
||||
warnings: &mut Vec<FormatWarning>,
|
||||
input_span: Span,
|
||||
) -> FormatArg {
|
||||
let (Ctx::RustcOnUnimplemented { tcx, trait_def_id }
|
||||
| Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = ctx;
|
||||
let trait_name = tcx.item_ident(*trait_def_id);
|
||||
let generics = tcx.generics_of(trait_def_id);
|
||||
let span = slice_span(input_span, arg.position_span);
|
||||
|
||||
match arg.position {
|
||||
// Something like "hello {name}"
|
||||
Position::ArgumentNamed(name) => match (ctx, Symbol::intern(name)) {
|
||||
// accepted, but deprecated
|
||||
(Ctx::RustcOnUnimplemented { .. }, sym::_Self) => {
|
||||
warnings
|
||||
.push(FormatWarning::FutureIncompat { span, help: String::from("use {Self}") });
|
||||
FormatArg::SelfUpper
|
||||
}
|
||||
(
|
||||
Ctx::RustcOnUnimplemented { .. },
|
||||
sym::from_desugaring
|
||||
| sym::crate_local
|
||||
| sym::direct
|
||||
| sym::cause
|
||||
| sym::float
|
||||
| sym::integer_
|
||||
| sym::integral,
|
||||
) => {
|
||||
warnings.push(FormatWarning::FutureIncompat {
|
||||
span,
|
||||
help: String::from("don't use this in a format string"),
|
||||
});
|
||||
FormatArg::AsIs(String::new())
|
||||
}
|
||||
|
||||
// Only `#[rustc_on_unimplemented]` can use these
|
||||
(Ctx::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext,
|
||||
(Ctx::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This,
|
||||
(Ctx::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait,
|
||||
// `{ThisTraitsName}`. Some attrs in std use this, but I'd like to change it to the more general `{This}`
|
||||
// because that'll be simpler to parse and extend in the future
|
||||
(Ctx::RustcOnUnimplemented { .. }, name) if name == trait_name.name => {
|
||||
warnings
|
||||
.push(FormatWarning::FutureIncompat { span, help: String::from("use {This}") });
|
||||
FormatArg::This
|
||||
}
|
||||
|
||||
// Any attribute can use these
|
||||
(
|
||||
Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. },
|
||||
kw::SelfUpper,
|
||||
) => FormatArg::SelfUpper,
|
||||
(
|
||||
Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. },
|
||||
generic_param,
|
||||
) if generics.own_params.iter().any(|param| param.name == generic_param) => {
|
||||
FormatArg::GenericParam { generic_param }
|
||||
}
|
||||
|
||||
(_, argument_name) => {
|
||||
warnings.push(FormatWarning::UnknownParam { argument_name, span });
|
||||
FormatArg::AsIs(format!("{{{}}}", argument_name.as_str()))
|
||||
}
|
||||
},
|
||||
|
||||
// `{:1}` and `{}` are ignored
|
||||
Position::ArgumentIs(idx) => {
|
||||
warnings.push(FormatWarning::PositionalArgument {
|
||||
span,
|
||||
help: format!("use `{{{idx}}}` to print a number in braces"),
|
||||
});
|
||||
FormatArg::AsIs(format!("{{{idx}}}"))
|
||||
}
|
||||
Position::ArgumentImplicitlyIs(_) => {
|
||||
warnings.push(FormatWarning::PositionalArgument {
|
||||
span,
|
||||
help: String::from("use `{{}}` to print empty braces"),
|
||||
});
|
||||
FormatArg::AsIs(String::from("{}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything
|
||||
/// with specifiers, so emit a warning if they are used.
|
||||
fn warn_on_format_spec(spec: FormatSpec<'_>, warnings: &mut Vec<FormatWarning>, input_span: Span) {
|
||||
if !matches!(
|
||||
spec,
|
||||
FormatSpec {
|
||||
fill: None,
|
||||
fill_span: None,
|
||||
align: Alignment::AlignUnknown,
|
||||
sign: None,
|
||||
alternate: false,
|
||||
zero_pad: false,
|
||||
debug_hex: None,
|
||||
precision: Count::CountImplied,
|
||||
precision_span: None,
|
||||
width: Count::CountImplied,
|
||||
width_span: None,
|
||||
ty: _,
|
||||
ty_span: _,
|
||||
},
|
||||
) {
|
||||
let span = spec.ty_span.map(|inner| slice_span(input_span, inner)).unwrap_or(input_span);
|
||||
warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() })
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function because `Span` and `rustc_parse_format::InnerSpan` don't know about each other
|
||||
fn slice_span(input: Span, inner: InnerSpan) -> Span {
|
||||
let InnerSpan { start, end } = inner;
|
||||
let span = input.data();
|
||||
|
||||
Span::new(
|
||||
span.lo + BytePos::from_usize(start),
|
||||
span.lo + BytePos::from_usize(end),
|
||||
span.ctxt,
|
||||
span.parent,
|
||||
)
|
||||
}
|
||||
|
||||
pub mod errors {
|
||||
use rustc_macros::LintDiagnostic;
|
||||
use rustc_span::Ident;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)]
|
||||
#[help]
|
||||
pub struct UnknownFormatParameterForOnUnimplementedAttr {
|
||||
pub argument_name: Symbol,
|
||||
pub trait_name: Ident,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(trait_selection_disallowed_positional_argument)]
|
||||
#[help]
|
||||
pub struct DisallowedPositionalArgument;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(trait_selection_invalid_format_specifier)]
|
||||
#[help]
|
||||
pub struct InvalidFormatSpecifier;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(trait_selection_missing_options_for_on_unimplemented_attr)]
|
||||
#[help]
|
||||
pub struct MissingOptionsForOnUnimplementedAttr;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(trait_selection_ignored_diagnostic_option)]
|
||||
pub struct IgnoredDiagnosticOption {
|
||||
pub option_name: &'static str,
|
||||
#[label]
|
||||
pub span: Span,
|
||||
#[label(trait_selection_other_label)]
|
||||
pub prev_span: Span,
|
||||
}
|
||||
|
||||
impl IgnoredDiagnosticOption {
|
||||
pub fn maybe_emit_warning<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item_def_id: DefId,
|
||||
new: Option<Span>,
|
||||
old: Option<Span>,
|
||||
option_name: &'static str,
|
||||
) {
|
||||
if let (Some(new_item), Some(old_item)) = (new, old) {
|
||||
if let Some(item_def_id) = item_def_id.as_local() {
|
||||
tcx.emit_node_span_lint(
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
tcx.local_def_id_to_hir_id(item_def_id),
|
||||
new_item,
|
||||
IgnoredDiagnosticOption {
|
||||
span: new_item,
|
||||
prev_span: old_item,
|
||||
option_name,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -950,7 +950,7 @@ impl fmt::Debug for ArgsOs {
|
|||
/// Constants associated with the current target
|
||||
#[stable(feature = "env", since = "1.0.0")]
|
||||
pub mod consts {
|
||||
use crate::sys::env::os;
|
||||
use crate::sys::env_consts::os;
|
||||
|
||||
/// A string describing the architecture of the CPU that is currently in use.
|
||||
/// An example value may be: `"x86"`, `"arm"` or `"riscv64"`.
|
||||
|
|
|
@ -1,106 +1,35 @@
|
|||
#[cfg(target_os = "linux")]
|
||||
//! Constants associated with each target.
|
||||
|
||||
// Replaces the #[else] gate with #[cfg(not(any(…)))] of all the other gates.
|
||||
// This ensures that they must be mutually exclusive and do not have precedence
|
||||
// like cfg_if!.
|
||||
macro cfg_unordered(
|
||||
$(#[cfg($cfg:meta)] $os:item)*
|
||||
#[else] $fallback:item
|
||||
) {
|
||||
$(#[cfg($cfg)] $os)*
|
||||
#[cfg(not(any($($cfg),*)))] $fallback
|
||||
}
|
||||
|
||||
// Keep entries sorted alphabetically and mutually exclusive.
|
||||
|
||||
cfg_unordered! {
|
||||
|
||||
#[cfg(target_os = "aix")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "linux";
|
||||
pub const OS: &str = "aix";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const DLL_SUFFIX: &str = ".a";
|
||||
pub const DLL_EXTENSION: &str = "a";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(target_os = "android")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "macos";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".dylib";
|
||||
pub const DLL_EXTENSION: &str = "dylib";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "ios";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".dylib";
|
||||
pub const DLL_EXTENSION: &str = "dylib";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "tvos")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "tvos";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".dylib";
|
||||
pub const DLL_EXTENSION: &str = "dylib";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "watchos")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "watchos";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".dylib";
|
||||
pub const DLL_EXTENSION: &str = "dylib";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "visionos")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "visionos";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".dylib";
|
||||
pub const DLL_EXTENSION: &str = "dylib";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "freebsd";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "dragonfly";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "netbsd")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "netbsd";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "openbsd")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "openbsd";
|
||||
pub const OS: &str = "android";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
|
@ -119,10 +48,10 @@ pub mod os {
|
|||
pub const EXE_EXTENSION: &str = "exe";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "android";
|
||||
pub const OS: &str = "dragonfly";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
|
@ -130,10 +59,21 @@ pub mod os {
|
|||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "solaris")]
|
||||
#[cfg(target_os = "emscripten")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "solaris";
|
||||
pub const OS: &str = "emscripten";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = ".js";
|
||||
pub const EXE_EXTENSION: &str = "js";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "espidf")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "espidf";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
|
@ -141,10 +81,21 @@ pub mod os {
|
|||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "illumos")]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "illumos";
|
||||
pub const OS: &str = "freebsd";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "fuchsia")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "fuchsia";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
|
@ -163,6 +114,17 @@ pub mod os {
|
|||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "hermit")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "";
|
||||
pub const OS: &str = "hermit";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = "";
|
||||
pub const DLL_EXTENSION: &str = "";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "horizon")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
|
@ -185,35 +147,24 @@ pub mod os {
|
|||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "vita")]
|
||||
#[cfg(target_os = "illumos")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "vita";
|
||||
pub const OS: &str = "illumos";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = ".elf";
|
||||
pub const EXE_EXTENSION: &str = "elf";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "emscripten", target_arch = "wasm32"))]
|
||||
#[cfg(target_os = "ios")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "emscripten";
|
||||
pub const OS: &str = "ios";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = ".js";
|
||||
pub const EXE_EXTENSION: &str = "js";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "fuchsia")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "fuchsia";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const DLL_SUFFIX: &str = ".dylib";
|
||||
pub const DLL_EXTENSION: &str = "dylib";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
@ -229,6 +180,39 @@ pub mod os {
|
|||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "linux";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "macos";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".dylib";
|
||||
pub const DLL_EXTENSION: &str = "dylib";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "netbsd")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "netbsd";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "nto")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
|
@ -240,6 +224,28 @@ pub mod os {
|
|||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "nuttx")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "nuttx";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "openbsd")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "openbsd";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
|
@ -262,6 +268,83 @@ pub mod os {
|
|||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "";
|
||||
pub const OS: &str = "";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = ".sgxs";
|
||||
pub const DLL_EXTENSION: &str = "sgxs";
|
||||
pub const EXE_SUFFIX: &str = ".sgxs";
|
||||
pub const EXE_EXTENSION: &str = "sgxs";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "solaris")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "solaris";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "solid_asp3")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "itron";
|
||||
pub const OS: &str = "solid";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "tvos")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "tvos";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".dylib";
|
||||
pub const DLL_EXTENSION: &str = "dylib";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "uefi")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "";
|
||||
pub const OS: &str = "uefi";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = "";
|
||||
pub const DLL_EXTENSION: &str = "";
|
||||
pub const EXE_SUFFIX: &str = ".efi";
|
||||
pub const EXE_EXTENSION: &str = "efi";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "visionos")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "visionos";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".dylib";
|
||||
pub const DLL_EXTENSION: &str = "dylib";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "vita")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "vita";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = ".elf";
|
||||
pub const EXE_EXTENSION: &str = "elf";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "vxworks")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
|
@ -273,35 +356,49 @@ pub mod os {
|
|||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "espidf")]
|
||||
#[cfg(all(target_family = "wasm", not(any(target_os = "emscripten", target_os = "linux"))))]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "";
|
||||
pub const OS: &str = "";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = ".wasm";
|
||||
pub const DLL_EXTENSION: &str = "wasm";
|
||||
pub const EXE_SUFFIX: &str = ".wasm";
|
||||
pub const EXE_EXTENSION: &str = "wasm";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "watchos")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "espidf";
|
||||
pub const OS: &str = "watchos";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const DLL_SUFFIX: &str = ".dylib";
|
||||
pub const DLL_EXTENSION: &str = "dylib";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "aix")]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "aix";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".a";
|
||||
pub const DLL_EXTENSION: &str = "a";
|
||||
pub const FAMILY: &str = "windows";
|
||||
pub const OS: &str = "windows";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = ".dll";
|
||||
pub const DLL_EXTENSION: &str = "dll";
|
||||
pub const EXE_SUFFIX: &str = ".exe";
|
||||
pub const EXE_EXTENSION: &str = "exe";
|
||||
}
|
||||
|
||||
// The fallback when none of the other gates match.
|
||||
#[else]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "";
|
||||
pub const OS: &str = "";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = "";
|
||||
pub const DLL_EXTENSION: &str = "";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
||||
|
||||
#[cfg(target_os = "nuttx")]
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "unix";
|
||||
pub const OS: &str = "nuttx";
|
||||
pub const DLL_PREFIX: &str = "lib";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
|
@ -12,6 +12,7 @@ pub mod anonymous_pipe;
|
|||
pub mod args;
|
||||
pub mod backtrace;
|
||||
pub mod cmath;
|
||||
pub mod env_consts;
|
||||
pub mod exit_guard;
|
||||
pub mod fd;
|
||||
pub mod fs;
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
pub mod os {
|
||||
pub const FAMILY: &str = "";
|
||||
pub const OS: &str = "hermit";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = "";
|
||||
pub const DLL_EXTENSION: &str = "";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
use crate::os::raw::c_char;
|
||||
|
||||
pub mod env;
|
||||
pub mod futex;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
pub mod os {
|
||||
pub const FAMILY: &str = "";
|
||||
pub const OS: &str = "";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = ".sgxs";
|
||||
pub const DLL_EXTENSION: &str = "sgxs";
|
||||
pub const EXE_SUFFIX: &str = ".sgxs";
|
||||
pub const EXE_EXTENSION: &str = "sgxs";
|
||||
}
|
|
@ -9,7 +9,6 @@ use crate::io::ErrorKind;
|
|||
use crate::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
pub mod abi;
|
||||
pub mod env;
|
||||
mod libunwind_integration;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
pub mod os {
|
||||
pub const FAMILY: &str = "itron";
|
||||
pub const OS: &str = "solid";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = ".so";
|
||||
pub const DLL_EXTENSION: &str = "so";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
|
@ -16,7 +16,6 @@ pub mod itron {
|
|||
use super::unsupported;
|
||||
}
|
||||
|
||||
pub mod env;
|
||||
// `error` is `pub(crate)` so that it can be accessed by `itron/error.rs` as
|
||||
// `crate::sys::error`
|
||||
pub(crate) mod error;
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
#![allow(unused_variables)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[path = "../unsupported/env.rs"]
|
||||
pub mod env;
|
||||
//pub mod fd;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
#[path = "../unsupported/common.rs"]
|
||||
#[deny(unsafe_op_in_unsafe_fn)]
|
||||
mod common;
|
||||
#[path = "../unsupported/env.rs"]
|
||||
pub mod env;
|
||||
#[path = "../unsupported/os.rs"]
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
pub mod os {
|
||||
pub const FAMILY: &str = "";
|
||||
pub const OS: &str = "uefi";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = "";
|
||||
pub const DLL_EXTENSION: &str = "";
|
||||
pub const EXE_SUFFIX: &str = ".efi";
|
||||
pub const EXE_EXTENSION: &str = "efi";
|
||||
}
|
|
@ -13,7 +13,6 @@
|
|||
//! [`OsString`]: crate::ffi::OsString
|
||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub mod env;
|
||||
pub mod helpers;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
|
|
|
@ -6,7 +6,6 @@ use crate::io::ErrorKind;
|
|||
#[macro_use]
|
||||
pub mod weak;
|
||||
|
||||
pub mod env;
|
||||
#[cfg(target_os = "fuchsia")]
|
||||
pub mod fuchsia;
|
||||
pub mod futex;
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
pub mod os {
|
||||
pub const FAMILY: &str = "";
|
||||
pub const OS: &str = "";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = "";
|
||||
pub const DLL_EXTENSION: &str = "";
|
||||
pub const EXE_SUFFIX: &str = "";
|
||||
pub const EXE_EXTENSION: &str = "";
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub mod env;
|
||||
pub mod os;
|
||||
pub mod pipe;
|
||||
pub mod thread;
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub mod os {
|
||||
pub const FAMILY: &str = "";
|
||||
pub const OS: &str = "";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = ".wasm";
|
||||
pub const DLL_EXTENSION: &str = "wasm";
|
||||
pub const EXE_SUFFIX: &str = ".wasm";
|
||||
pub const EXE_EXTENSION: &str = "wasm";
|
||||
}
|
|
@ -13,7 +13,6 @@
|
|||
//! compiling for wasm. That way it's a compile time error for something that's
|
||||
//! guaranteed to be a runtime error!
|
||||
|
||||
pub mod env;
|
||||
#[allow(unused)]
|
||||
#[path = "../wasm/atomics/futex.rs"]
|
||||
pub mod futex;
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
//! To begin with, this target mirrors the wasi target 1 to 1, but over
|
||||
//! time this will change significantly.
|
||||
|
||||
#[path = "../wasi/env.rs"]
|
||||
pub mod env;
|
||||
#[allow(unused)]
|
||||
#[path = "../wasm/atomics/futex.rs"]
|
||||
pub mod futex;
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
pub mod os {
|
||||
pub const FAMILY: &str = "";
|
||||
pub const OS: &str = "";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = ".wasm";
|
||||
pub const DLL_EXTENSION: &str = "wasm";
|
||||
pub const EXE_SUFFIX: &str = ".wasm";
|
||||
pub const EXE_EXTENSION: &str = "wasm";
|
||||
}
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub mod env;
|
||||
#[path = "../unsupported/os.rs"]
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
pub mod os {
|
||||
pub const FAMILY: &str = "windows";
|
||||
pub const OS: &str = "windows";
|
||||
pub const DLL_PREFIX: &str = "";
|
||||
pub const DLL_SUFFIX: &str = ".dll";
|
||||
pub const DLL_EXTENSION: &str = "dll";
|
||||
pub const EXE_SUFFIX: &str = ".exe";
|
||||
pub const EXE_EXTENSION: &str = "exe";
|
||||
}
|
|
@ -15,7 +15,6 @@ pub mod compat;
|
|||
pub mod api;
|
||||
|
||||
pub mod c;
|
||||
pub mod env;
|
||||
#[cfg(not(target_vendor = "win7"))]
|
||||
pub mod futex;
|
||||
pub mod handle;
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
#[path = "../unsupported/env.rs"]
|
||||
pub mod env;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
pub const WORD_SIZE: usize = size_of::<u32>();
|
||||
|
||||
pub mod abi;
|
||||
pub mod env;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
use std::fmt::{self, Display};
|
||||
use std::hash::Hash;
|
||||
use std::io::IsTerminal;
|
||||
use std::path::{Path, PathBuf, absolute};
|
||||
use std::process::Command;
|
||||
|
@ -701,6 +702,7 @@ pub(crate) struct TomlConfig {
|
|||
target: Option<HashMap<String, TomlTarget>>,
|
||||
dist: Option<Dist>,
|
||||
profile: Option<String>,
|
||||
include: Option<Vec<PathBuf>>,
|
||||
}
|
||||
|
||||
/// This enum is used for deserializing change IDs from TOML, allowing both numeric values and the string `"ignore"`.
|
||||
|
@ -747,27 +749,35 @@ enum ReplaceOpt {
|
|||
}
|
||||
|
||||
trait Merge {
|
||||
fn merge(&mut self, other: Self, replace: ReplaceOpt);
|
||||
fn merge(
|
||||
&mut self,
|
||||
parent_config_path: Option<PathBuf>,
|
||||
included_extensions: &mut HashSet<PathBuf>,
|
||||
other: Self,
|
||||
replace: ReplaceOpt,
|
||||
);
|
||||
}
|
||||
|
||||
impl Merge for TomlConfig {
|
||||
fn merge(
|
||||
&mut self,
|
||||
TomlConfig { build, install, llvm, gcc, rust, dist, target, profile, change_id }: Self,
|
||||
parent_config_path: Option<PathBuf>,
|
||||
included_extensions: &mut HashSet<PathBuf>,
|
||||
TomlConfig { build, install, llvm, gcc, rust, dist, target, profile, change_id, include }: Self,
|
||||
replace: ReplaceOpt,
|
||||
) {
|
||||
fn do_merge<T: Merge>(x: &mut Option<T>, y: Option<T>, replace: ReplaceOpt) {
|
||||
if let Some(new) = y {
|
||||
if let Some(original) = x {
|
||||
original.merge(new, replace);
|
||||
original.merge(None, &mut Default::default(), new, replace);
|
||||
} else {
|
||||
*x = Some(new);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.change_id.inner.merge(change_id.inner, replace);
|
||||
self.profile.merge(profile, replace);
|
||||
self.change_id.inner.merge(None, &mut Default::default(), change_id.inner, replace);
|
||||
self.profile.merge(None, &mut Default::default(), profile, replace);
|
||||
|
||||
do_merge(&mut self.build, build, replace);
|
||||
do_merge(&mut self.install, install, replace);
|
||||
|
@ -782,13 +792,50 @@ impl Merge for TomlConfig {
|
|||
(Some(original_target), Some(new_target)) => {
|
||||
for (triple, new) in new_target {
|
||||
if let Some(original) = original_target.get_mut(&triple) {
|
||||
original.merge(new, replace);
|
||||
original.merge(None, &mut Default::default(), new, replace);
|
||||
} else {
|
||||
original_target.insert(triple, new);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let parent_dir = parent_config_path
|
||||
.as_ref()
|
||||
.and_then(|p| p.parent().map(ToOwned::to_owned))
|
||||
.unwrap_or_default();
|
||||
|
||||
// `include` handled later since we ignore duplicates using `ReplaceOpt::IgnoreDuplicate` to
|
||||
// keep the upper-level configuration to take precedence.
|
||||
for include_path in include.clone().unwrap_or_default().iter().rev() {
|
||||
let include_path = parent_dir.join(include_path);
|
||||
let include_path = include_path.canonicalize().unwrap_or_else(|e| {
|
||||
eprintln!("ERROR: Failed to canonicalize '{}' path: {e}", include_path.display());
|
||||
exit!(2);
|
||||
});
|
||||
|
||||
let included_toml = Config::get_toml_inner(&include_path).unwrap_or_else(|e| {
|
||||
eprintln!("ERROR: Failed to parse '{}': {e}", include_path.display());
|
||||
exit!(2);
|
||||
});
|
||||
|
||||
assert!(
|
||||
included_extensions.insert(include_path.clone()),
|
||||
"Cyclic inclusion detected: '{}' is being included again before its previous inclusion was fully processed.",
|
||||
include_path.display()
|
||||
);
|
||||
|
||||
self.merge(
|
||||
Some(include_path.clone()),
|
||||
included_extensions,
|
||||
included_toml,
|
||||
// Ensures that parent configuration always takes precedence
|
||||
// over child configurations.
|
||||
ReplaceOpt::IgnoreDuplicate,
|
||||
);
|
||||
|
||||
included_extensions.remove(&include_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -803,7 +850,13 @@ macro_rules! define_config {
|
|||
}
|
||||
|
||||
impl Merge for $name {
|
||||
fn merge(&mut self, other: Self, replace: ReplaceOpt) {
|
||||
fn merge(
|
||||
&mut self,
|
||||
_parent_config_path: Option<PathBuf>,
|
||||
_included_extensions: &mut HashSet<PathBuf>,
|
||||
other: Self,
|
||||
replace: ReplaceOpt
|
||||
) {
|
||||
$(
|
||||
match replace {
|
||||
ReplaceOpt::IgnoreDuplicate => {
|
||||
|
@ -903,7 +956,13 @@ macro_rules! define_config {
|
|||
}
|
||||
|
||||
impl<T> Merge for Option<T> {
|
||||
fn merge(&mut self, other: Self, replace: ReplaceOpt) {
|
||||
fn merge(
|
||||
&mut self,
|
||||
_parent_config_path: Option<PathBuf>,
|
||||
_included_extensions: &mut HashSet<PathBuf>,
|
||||
other: Self,
|
||||
replace: ReplaceOpt,
|
||||
) {
|
||||
match replace {
|
||||
ReplaceOpt::IgnoreDuplicate => {
|
||||
if self.is_none() {
|
||||
|
@ -1363,13 +1422,15 @@ impl Config {
|
|||
Self::get_toml(&builder_config_path)
|
||||
}
|
||||
|
||||
pub(crate) fn get_toml(file: &Path) -> Result<TomlConfig, toml::de::Error> {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn get_toml(_: &Path) -> Result<TomlConfig, toml::de::Error> {
|
||||
Ok(TomlConfig::default())
|
||||
}
|
||||
return Ok(TomlConfig::default());
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub(crate) fn get_toml(file: &Path) -> Result<TomlConfig, toml::de::Error> {
|
||||
Self::get_toml_inner(file)
|
||||
}
|
||||
|
||||
fn get_toml_inner(file: &Path) -> Result<TomlConfig, toml::de::Error> {
|
||||
let contents =
|
||||
t!(fs::read_to_string(file), format!("config file {} not found", file.display()));
|
||||
// Deserialize to Value and then TomlConfig to prevent the Deserialize impl of
|
||||
|
@ -1548,7 +1609,8 @@ impl Config {
|
|||
// but not if `bootstrap.toml` hasn't been created.
|
||||
let mut toml = if !using_default_path || toml_path.exists() {
|
||||
config.config = Some(if cfg!(not(test)) {
|
||||
toml_path.canonicalize().unwrap()
|
||||
toml_path = toml_path.canonicalize().unwrap();
|
||||
toml_path.clone()
|
||||
} else {
|
||||
toml_path.clone()
|
||||
});
|
||||
|
@ -1576,6 +1638,26 @@ impl Config {
|
|||
toml.profile = Some("dist".into());
|
||||
}
|
||||
|
||||
// Reverse the list to ensure the last added config extension remains the most dominant.
|
||||
// For example, given ["a.toml", "b.toml"], "b.toml" should take precedence over "a.toml".
|
||||
//
|
||||
// This must be handled before applying the `profile` since `include`s should always take
|
||||
// precedence over `profile`s.
|
||||
for include_path in toml.include.clone().unwrap_or_default().iter().rev() {
|
||||
let include_path = toml_path.parent().unwrap().join(include_path);
|
||||
|
||||
let included_toml = get_toml(&include_path).unwrap_or_else(|e| {
|
||||
eprintln!("ERROR: Failed to parse '{}': {e}", include_path.display());
|
||||
exit!(2);
|
||||
});
|
||||
toml.merge(
|
||||
Some(include_path),
|
||||
&mut Default::default(),
|
||||
included_toml,
|
||||
ReplaceOpt::IgnoreDuplicate,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(include) = &toml.profile {
|
||||
// Allows creating alias for profile names, allowing
|
||||
// profiles to be renamed while maintaining back compatibility
|
||||
|
@ -1597,7 +1679,12 @@ impl Config {
|
|||
);
|
||||
exit!(2);
|
||||
});
|
||||
toml.merge(included_toml, ReplaceOpt::IgnoreDuplicate);
|
||||
toml.merge(
|
||||
Some(include_path),
|
||||
&mut Default::default(),
|
||||
included_toml,
|
||||
ReplaceOpt::IgnoreDuplicate,
|
||||
);
|
||||
}
|
||||
|
||||
let mut override_toml = TomlConfig::default();
|
||||
|
@ -1608,7 +1695,12 @@ impl Config {
|
|||
|
||||
let mut err = match get_table(option) {
|
||||
Ok(v) => {
|
||||
override_toml.merge(v, ReplaceOpt::ErrorOnDuplicate);
|
||||
override_toml.merge(
|
||||
None,
|
||||
&mut Default::default(),
|
||||
v,
|
||||
ReplaceOpt::ErrorOnDuplicate,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Err(e) => e,
|
||||
|
@ -1619,7 +1711,12 @@ impl Config {
|
|||
if !value.contains('"') {
|
||||
match get_table(&format!(r#"{key}="{value}""#)) {
|
||||
Ok(v) => {
|
||||
override_toml.merge(v, ReplaceOpt::ErrorOnDuplicate);
|
||||
override_toml.merge(
|
||||
None,
|
||||
&mut Default::default(),
|
||||
v,
|
||||
ReplaceOpt::ErrorOnDuplicate,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Err(e) => err = e,
|
||||
|
@ -1629,7 +1726,7 @@ impl Config {
|
|||
eprintln!("failed to parse override `{option}`: `{err}");
|
||||
exit!(2)
|
||||
}
|
||||
toml.merge(override_toml, ReplaceOpt::Override);
|
||||
toml.merge(None, &mut Default::default(), override_toml, ReplaceOpt::Override);
|
||||
|
||||
config.change_id = toml.change_id.inner;
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::collections::BTreeSet;
|
||||
use std::env;
|
||||
use std::fs::{File, remove_file};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{env, fs};
|
||||
|
||||
use build_helper::ci::CiEnv;
|
||||
use clap::CommandFactory;
|
||||
|
@ -23,6 +23,27 @@ pub(crate) fn parse(config: &str) -> Config {
|
|||
)
|
||||
}
|
||||
|
||||
fn get_toml(file: &Path) -> Result<TomlConfig, toml::de::Error> {
|
||||
let contents = std::fs::read_to_string(file).unwrap();
|
||||
toml::from_str(&contents).and_then(|table: toml::Value| TomlConfig::deserialize(table))
|
||||
}
|
||||
|
||||
/// Helps with debugging by using consistent test-specific directories instead of
|
||||
/// random temporary directories.
|
||||
fn prepare_test_specific_dir() -> PathBuf {
|
||||
let current = std::thread::current();
|
||||
// Replace "::" with "_" to make it safe for directory names on Windows systems
|
||||
let test_path = current.name().unwrap().replace("::", "_");
|
||||
|
||||
let testdir = parse("").tempdir().join(test_path);
|
||||
|
||||
// clean up any old test files
|
||||
let _ = fs::remove_dir_all(&testdir);
|
||||
let _ = fs::create_dir_all(&testdir);
|
||||
|
||||
testdir
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn download_ci_llvm() {
|
||||
let config = parse("llvm.download-ci-llvm = false");
|
||||
|
@ -539,3 +560,189 @@ fn test_ci_flag() {
|
|||
let config = Config::parse_inner(Flags::parse(&["check".into()]), |&_| toml::from_str(""));
|
||||
assert_eq!(config.is_running_on_ci, CiEnv::is_ci());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_precedence_of_includes() {
|
||||
let testdir = prepare_test_specific_dir();
|
||||
|
||||
let root_config = testdir.join("config.toml");
|
||||
let root_config_content = br#"
|
||||
include = ["./extension.toml"]
|
||||
|
||||
[llvm]
|
||||
link-jobs = 2
|
||||
"#;
|
||||
File::create(&root_config).unwrap().write_all(root_config_content).unwrap();
|
||||
|
||||
let extension = testdir.join("extension.toml");
|
||||
let extension_content = br#"
|
||||
change-id=543
|
||||
include = ["./extension2.toml"]
|
||||
"#;
|
||||
File::create(extension).unwrap().write_all(extension_content).unwrap();
|
||||
|
||||
let extension = testdir.join("extension2.toml");
|
||||
let extension_content = br#"
|
||||
change-id=742
|
||||
|
||||
[llvm]
|
||||
link-jobs = 10
|
||||
|
||||
[build]
|
||||
description = "Some creative description"
|
||||
"#;
|
||||
File::create(extension).unwrap().write_all(extension_content).unwrap();
|
||||
|
||||
let config = Config::parse_inner(
|
||||
Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]),
|
||||
get_toml,
|
||||
);
|
||||
|
||||
assert_eq!(config.change_id.unwrap(), ChangeId::Id(543));
|
||||
assert_eq!(config.llvm_link_jobs.unwrap(), 2);
|
||||
assert_eq!(config.description.unwrap(), "Some creative description");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Cyclic inclusion detected")]
|
||||
fn test_cyclic_include_direct() {
|
||||
let testdir = prepare_test_specific_dir();
|
||||
|
||||
let root_config = testdir.join("config.toml");
|
||||
let root_config_content = br#"
|
||||
include = ["./extension.toml"]
|
||||
"#;
|
||||
File::create(&root_config).unwrap().write_all(root_config_content).unwrap();
|
||||
|
||||
let extension = testdir.join("extension.toml");
|
||||
let extension_content = br#"
|
||||
include = ["./config.toml"]
|
||||
"#;
|
||||
File::create(extension).unwrap().write_all(extension_content).unwrap();
|
||||
|
||||
let config = Config::parse_inner(
|
||||
Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]),
|
||||
get_toml,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Cyclic inclusion detected")]
|
||||
fn test_cyclic_include_indirect() {
|
||||
let testdir = prepare_test_specific_dir();
|
||||
|
||||
let root_config = testdir.join("config.toml");
|
||||
let root_config_content = br#"
|
||||
include = ["./extension.toml"]
|
||||
"#;
|
||||
File::create(&root_config).unwrap().write_all(root_config_content).unwrap();
|
||||
|
||||
let extension = testdir.join("extension.toml");
|
||||
let extension_content = br#"
|
||||
include = ["./extension2.toml"]
|
||||
"#;
|
||||
File::create(extension).unwrap().write_all(extension_content).unwrap();
|
||||
|
||||
let extension = testdir.join("extension2.toml");
|
||||
let extension_content = br#"
|
||||
include = ["./extension3.toml"]
|
||||
"#;
|
||||
File::create(extension).unwrap().write_all(extension_content).unwrap();
|
||||
|
||||
let extension = testdir.join("extension3.toml");
|
||||
let extension_content = br#"
|
||||
include = ["./extension.toml"]
|
||||
"#;
|
||||
File::create(extension).unwrap().write_all(extension_content).unwrap();
|
||||
|
||||
let config = Config::parse_inner(
|
||||
Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]),
|
||||
get_toml,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_include_absolute_paths() {
|
||||
let testdir = prepare_test_specific_dir();
|
||||
|
||||
let extension = testdir.join("extension.toml");
|
||||
File::create(&extension).unwrap().write_all(&[]).unwrap();
|
||||
|
||||
let root_config = testdir.join("config.toml");
|
||||
let extension_absolute_path =
|
||||
extension.canonicalize().unwrap().to_str().unwrap().replace('\\', r"\\");
|
||||
let root_config_content = format!(r#"include = ["{}"]"#, extension_absolute_path);
|
||||
File::create(&root_config).unwrap().write_all(root_config_content.as_bytes()).unwrap();
|
||||
|
||||
let config = Config::parse_inner(
|
||||
Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]),
|
||||
get_toml,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_include_relative_paths() {
|
||||
let testdir = prepare_test_specific_dir();
|
||||
|
||||
let _ = fs::create_dir_all(&testdir.join("subdir/another_subdir"));
|
||||
|
||||
let root_config = testdir.join("config.toml");
|
||||
let root_config_content = br#"
|
||||
include = ["./subdir/extension.toml"]
|
||||
"#;
|
||||
File::create(&root_config).unwrap().write_all(root_config_content).unwrap();
|
||||
|
||||
let extension = testdir.join("subdir/extension.toml");
|
||||
let extension_content = br#"
|
||||
include = ["../extension2.toml"]
|
||||
"#;
|
||||
File::create(extension).unwrap().write_all(extension_content).unwrap();
|
||||
|
||||
let extension = testdir.join("extension2.toml");
|
||||
let extension_content = br#"
|
||||
include = ["./subdir/another_subdir/extension3.toml"]
|
||||
"#;
|
||||
File::create(extension).unwrap().write_all(extension_content).unwrap();
|
||||
|
||||
let extension = testdir.join("subdir/another_subdir/extension3.toml");
|
||||
let extension_content = br#"
|
||||
include = ["../../extension4.toml"]
|
||||
"#;
|
||||
File::create(extension).unwrap().write_all(extension_content).unwrap();
|
||||
|
||||
let extension = testdir.join("extension4.toml");
|
||||
File::create(extension).unwrap().write_all(&[]).unwrap();
|
||||
|
||||
let config = Config::parse_inner(
|
||||
Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]),
|
||||
get_toml,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_include_precedence_over_profile() {
|
||||
let testdir = prepare_test_specific_dir();
|
||||
|
||||
let root_config = testdir.join("config.toml");
|
||||
let root_config_content = br#"
|
||||
profile = "dist"
|
||||
include = ["./extension.toml"]
|
||||
"#;
|
||||
File::create(&root_config).unwrap().write_all(root_config_content).unwrap();
|
||||
|
||||
let extension = testdir.join("extension.toml");
|
||||
let extension_content = br#"
|
||||
[rust]
|
||||
channel = "dev"
|
||||
"#;
|
||||
File::create(extension).unwrap().write_all(extension_content).unwrap();
|
||||
|
||||
let config = Config::parse_inner(
|
||||
Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]),
|
||||
get_toml,
|
||||
);
|
||||
|
||||
// "dist" profile would normally set the channel to "auto-detect", but includes should
|
||||
// override profile settings, so we expect this to be "dev" here.
|
||||
assert_eq!(config.channel, "dev");
|
||||
}
|
||||
|
|
|
@ -396,4 +396,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
|
|||
severity: ChangeSeverity::Info,
|
||||
summary: "Added a new option `build.compiletest-use-stage0-libtest` to force `compiletest` to use the stage 0 libtest.",
|
||||
},
|
||||
ChangeInfo {
|
||||
change_id: 138934,
|
||||
severity: ChangeSeverity::Info,
|
||||
summary: "Added new option `include` to create config extensions.",
|
||||
},
|
||||
];
|
||||
|
|
|
@ -64,12 +64,63 @@ version = "1.0.95"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
|
||||
[[package]]
|
||||
name = "askama"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7"
|
||||
dependencies = [
|
||||
"askama_derive",
|
||||
"itoa",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_derive"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac"
|
||||
dependencies = [
|
||||
"askama_parser",
|
||||
"basic-toml",
|
||||
"memchr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_parser"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "basic-toml"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "build_helper"
|
||||
version = "0.1.0"
|
||||
|
@ -104,6 +155,7 @@ name = "citool"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"askama",
|
||||
"build_helper",
|
||||
"clap",
|
||||
"csv",
|
||||
|
@ -646,6 +698,12 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.23"
|
||||
|
@ -1026,6 +1084,15 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
|
|
|
@ -5,6 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
askama = "0.13"
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
csv = "1"
|
||||
diff = "0.1"
|
||||
|
|
|
@ -8,9 +8,9 @@ use build_helper::metrics::{
|
|||
};
|
||||
|
||||
use crate::github::JobInfoResolver;
|
||||
use crate::metrics;
|
||||
use crate::metrics::{JobMetrics, JobName, get_test_suites};
|
||||
use crate::utils::{output_details, pluralize};
|
||||
use crate::{metrics, utils};
|
||||
|
||||
/// Outputs durations of individual bootstrap steps from the gathered bootstrap invocations,
|
||||
/// and also a table with summarized information about executed tests.
|
||||
|
@ -394,18 +394,17 @@ fn aggregate_tests(metrics: &JsonRoot) -> TestSuiteData {
|
|||
// Poor man's detection of doctests based on the "(line XYZ)" suffix
|
||||
let is_doctest = matches!(suite.metadata, TestSuiteMetadata::CargoPackage { .. })
|
||||
&& test.name.contains("(line");
|
||||
let test_entry = Test { name: generate_test_name(&test.name), stage, is_doctest };
|
||||
let test_entry = Test {
|
||||
name: utils::normalize_path_delimiters(&test.name).to_string(),
|
||||
stage,
|
||||
is_doctest,
|
||||
};
|
||||
tests.insert(test_entry, test.outcome.clone());
|
||||
}
|
||||
}
|
||||
TestSuiteData { tests }
|
||||
}
|
||||
|
||||
/// Normalizes Windows-style path delimiters to Unix-style paths.
|
||||
fn generate_test_name(name: &str) -> String {
|
||||
name.replace('\\', "/")
|
||||
}
|
||||
|
||||
/// Prints test changes in Markdown format to stdout.
|
||||
fn report_test_diffs(
|
||||
diff: AggregatedTestDiffs,
|
||||
|
|
|
@ -4,6 +4,7 @@ mod datadog;
|
|||
mod github;
|
||||
mod jobs;
|
||||
mod metrics;
|
||||
mod test_dashboard;
|
||||
mod utils;
|
||||
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
@ -22,7 +23,8 @@ use crate::datadog::upload_datadog_metric;
|
|||
use crate::github::JobInfoResolver;
|
||||
use crate::jobs::RunType;
|
||||
use crate::metrics::{JobMetrics, download_auto_job_metrics, download_job_metrics, load_metrics};
|
||||
use crate::utils::load_env_var;
|
||||
use crate::test_dashboard::generate_test_dashboard;
|
||||
use crate::utils::{load_env_var, output_details};
|
||||
|
||||
const CI_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/..");
|
||||
const DOCKER_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../docker");
|
||||
|
@ -180,12 +182,26 @@ fn postprocess_metrics(
|
|||
}
|
||||
|
||||
fn post_merge_report(db: JobDatabase, current: String, parent: String) -> anyhow::Result<()> {
|
||||
let metrics = download_auto_job_metrics(&db, &parent, ¤t)?;
|
||||
let metrics = download_auto_job_metrics(&db, Some(&parent), ¤t)?;
|
||||
|
||||
println!("\nComparing {parent} (parent) -> {current} (this PR)\n");
|
||||
|
||||
let mut job_info_resolver = JobInfoResolver::new();
|
||||
output_test_diffs(&metrics, &mut job_info_resolver);
|
||||
|
||||
output_details("Test dashboard", || {
|
||||
println!(
|
||||
r#"\nRun
|
||||
|
||||
```bash
|
||||
cargo run --manifest-path src/ci/citool/Cargo.toml -- \
|
||||
test-dashboard {current} --output-dir test-dashboard
|
||||
```
|
||||
And then open `test-dashboard/index.html` in your browser to see an overview of all executed tests.
|
||||
"#
|
||||
);
|
||||
});
|
||||
|
||||
output_largest_duration_changes(&metrics, &mut job_info_resolver);
|
||||
|
||||
Ok(())
|
||||
|
@ -234,6 +250,14 @@ enum Args {
|
|||
/// Current commit that will be compared to `parent`.
|
||||
current: String,
|
||||
},
|
||||
/// Generate a directory containing a HTML dashboard of test results from a CI run.
|
||||
TestDashboard {
|
||||
/// Commit SHA that was tested on CI to analyze.
|
||||
current: String,
|
||||
/// Output path for the HTML directory.
|
||||
#[clap(long)]
|
||||
output_dir: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(clap::ValueEnum, Clone)]
|
||||
|
@ -275,7 +299,11 @@ fn main() -> anyhow::Result<()> {
|
|||
postprocess_metrics(metrics_path, parent, job_name)?;
|
||||
}
|
||||
Args::PostMergeReport { current, parent } => {
|
||||
post_merge_report(load_db(default_jobs_file)?, current, parent)?;
|
||||
post_merge_report(load_db(&default_jobs_file)?, current, parent)?;
|
||||
}
|
||||
Args::TestDashboard { current, output_dir } => {
|
||||
let db = load_db(&default_jobs_file)?;
|
||||
generate_test_dashboard(db, ¤t, &output_dir)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,14 +46,15 @@ pub struct JobMetrics {
|
|||
/// `parent` and `current` should be commit SHAs.
|
||||
pub fn download_auto_job_metrics(
|
||||
job_db: &JobDatabase,
|
||||
parent: &str,
|
||||
parent: Option<&str>,
|
||||
current: &str,
|
||||
) -> anyhow::Result<HashMap<JobName, JobMetrics>> {
|
||||
let mut jobs = HashMap::default();
|
||||
|
||||
for job in &job_db.auto_jobs {
|
||||
eprintln!("Downloading metrics of job {}", job.name);
|
||||
let metrics_parent = match download_job_metrics(&job.name, parent) {
|
||||
let metrics_parent =
|
||||
parent.and_then(|parent| match download_job_metrics(&job.name, parent) {
|
||||
Ok(metrics) => Some(metrics),
|
||||
Err(error) => {
|
||||
eprintln!(
|
||||
|
@ -63,7 +64,7 @@ Maybe it was newly added?"#,
|
|||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
});
|
||||
let metrics_current = download_job_metrics(&job.name, current)?;
|
||||
jobs.insert(
|
||||
job.name.clone(),
|
||||
|
|
216
src/ci/citool/src/test_dashboard.rs
Normal file
216
src/ci/citool/src/test_dashboard.rs
Normal file
|
@ -0,0 +1,216 @@
|
|||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fs::File;
|
||||
use std::io::BufWriter;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use askama::Template;
|
||||
use build_helper::metrics::{TestOutcome, TestSuiteMetadata};
|
||||
|
||||
use crate::jobs::JobDatabase;
|
||||
use crate::metrics::{JobMetrics, JobName, download_auto_job_metrics, get_test_suites};
|
||||
use crate::utils::normalize_path_delimiters;
|
||||
|
||||
/// Generate a set of HTML files into a directory that contain a dashboard of test results.
|
||||
pub fn generate_test_dashboard(
|
||||
db: JobDatabase,
|
||||
current: &str,
|
||||
output_dir: &Path,
|
||||
) -> anyhow::Result<()> {
|
||||
let metrics = download_auto_job_metrics(&db, None, current)?;
|
||||
let suites = gather_test_suites(&metrics);
|
||||
|
||||
std::fs::create_dir_all(output_dir)?;
|
||||
|
||||
let test_count = suites.test_count();
|
||||
write_page(output_dir, "index.html", &TestSuitesPage { suites, test_count })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_page<T: Template>(dir: &Path, name: &str, template: &T) -> anyhow::Result<()> {
|
||||
let mut file = BufWriter::new(File::create(dir.join(name))?);
|
||||
Template::write_into(template, &mut file)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gather_test_suites(job_metrics: &HashMap<JobName, JobMetrics>) -> TestSuites {
|
||||
struct CoarseTestSuite<'a> {
|
||||
tests: BTreeMap<String, Test<'a>>,
|
||||
}
|
||||
|
||||
let mut suites: HashMap<String, CoarseTestSuite> = HashMap::new();
|
||||
|
||||
// First, gather tests from all jobs, stages and targets, and aggregate them per suite
|
||||
// Only work with compiletest suites.
|
||||
for (job, metrics) in job_metrics {
|
||||
let test_suites = get_test_suites(&metrics.current);
|
||||
for suite in test_suites {
|
||||
let (suite_name, stage, target) = match &suite.metadata {
|
||||
TestSuiteMetadata::CargoPackage { .. } => {
|
||||
continue;
|
||||
}
|
||||
TestSuiteMetadata::Compiletest { suite, stage, target, .. } => {
|
||||
(suite.clone(), *stage, target)
|
||||
}
|
||||
};
|
||||
let suite_entry = suites
|
||||
.entry(suite_name.clone())
|
||||
.or_insert_with(|| CoarseTestSuite { tests: Default::default() });
|
||||
let test_metadata = TestMetadata { job, stage, target };
|
||||
|
||||
for test in &suite.tests {
|
||||
let test_name = normalize_test_name(&test.name, &suite_name);
|
||||
let (test_name, variant_name) = match test_name.rsplit_once('#') {
|
||||
Some((name, variant)) => (name.to_string(), variant.to_string()),
|
||||
None => (test_name, "".to_string()),
|
||||
};
|
||||
let test_entry = suite_entry
|
||||
.tests
|
||||
.entry(test_name.clone())
|
||||
.or_insert_with(|| Test { revisions: Default::default() });
|
||||
let variant_entry = test_entry
|
||||
.revisions
|
||||
.entry(variant_name)
|
||||
.or_insert_with(|| TestResults { passed: vec![], ignored: vec![] });
|
||||
|
||||
match test.outcome {
|
||||
TestOutcome::Passed => {
|
||||
variant_entry.passed.push(test_metadata);
|
||||
}
|
||||
TestOutcome::Ignored { ignore_reason: _ } => {
|
||||
variant_entry.ignored.push(test_metadata);
|
||||
}
|
||||
TestOutcome::Failed => {
|
||||
eprintln!("Warning: failed test {test_name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then, split the suites per directory
|
||||
let mut suites = suites.into_iter().collect::<Vec<_>>();
|
||||
suites.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
|
||||
let suites = suites
|
||||
.into_iter()
|
||||
.map(|(suite_name, suite)| TestSuite { group: build_test_group(&suite_name, suite.tests) })
|
||||
.collect();
|
||||
|
||||
TestSuites { suites }
|
||||
}
|
||||
|
||||
/// Recursively expand a test group based on filesystem hierarchy.
|
||||
fn build_test_group<'a>(name: &str, tests: BTreeMap<String, Test<'a>>) -> TestGroup<'a> {
|
||||
let mut root_tests = vec![];
|
||||
let mut subdirs: BTreeMap<String, BTreeMap<String, Test<'a>>> = Default::default();
|
||||
|
||||
// Split tests into root tests and tests located in subdirectories
|
||||
for (name, test) in tests {
|
||||
let mut components = Path::new(&name).components().peekable();
|
||||
let subdir = components.next().unwrap();
|
||||
|
||||
if components.peek().is_none() {
|
||||
// This is a root test
|
||||
root_tests.push((name, test));
|
||||
} else {
|
||||
// This is a test in a nested directory
|
||||
let subdir_tests =
|
||||
subdirs.entry(subdir.as_os_str().to_str().unwrap().to_string()).or_default();
|
||||
let test_name =
|
||||
components.into_iter().collect::<PathBuf>().to_str().unwrap().to_string();
|
||||
subdir_tests.insert(test_name, test);
|
||||
}
|
||||
}
|
||||
let dirs = subdirs
|
||||
.into_iter()
|
||||
.map(|(name, tests)| {
|
||||
let group = build_test_group(&name, tests);
|
||||
(name, group)
|
||||
})
|
||||
.collect();
|
||||
|
||||
TestGroup { name: name.to_string(), root_tests, groups: dirs }
|
||||
}
|
||||
|
||||
/// Compiletest tests start with `[suite] tests/[suite]/a/b/c...`.
|
||||
/// Remove the `[suite] tests/[suite]/` prefix so that we can find the filesystem path.
|
||||
/// Also normalizes path delimiters.
|
||||
fn normalize_test_name(name: &str, suite_name: &str) -> String {
|
||||
let name = normalize_path_delimiters(name);
|
||||
let name = name.as_ref();
|
||||
let name = name.strip_prefix(&format!("[{suite_name}]")).unwrap_or(name).trim();
|
||||
let name = name.strip_prefix("tests/").unwrap_or(name);
|
||||
let name = name.strip_prefix(suite_name).unwrap_or(name);
|
||||
name.trim_start_matches("/").to_string()
|
||||
}
|
||||
|
||||
struct TestSuites<'a> {
|
||||
suites: Vec<TestSuite<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> TestSuites<'a> {
|
||||
fn test_count(&self) -> u64 {
|
||||
self.suites.iter().map(|suite| suite.group.test_count()).sum::<u64>()
|
||||
}
|
||||
}
|
||||
|
||||
struct TestSuite<'a> {
|
||||
group: TestGroup<'a>,
|
||||
}
|
||||
|
||||
struct TestResults<'a> {
|
||||
passed: Vec<TestMetadata<'a>>,
|
||||
ignored: Vec<TestMetadata<'a>>,
|
||||
}
|
||||
|
||||
struct Test<'a> {
|
||||
revisions: BTreeMap<String, TestResults<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Test<'a> {
|
||||
/// If this is a test without revisions, it will have a single entry in `revisions` with
|
||||
/// an empty string as the revision name.
|
||||
fn single_test(&self) -> Option<&TestResults<'a>> {
|
||||
if self.revisions.len() == 1 {
|
||||
self.revisions.iter().next().take_if(|e| e.0.is_empty()).map(|e| e.1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[allow(dead_code)]
|
||||
struct TestMetadata<'a> {
|
||||
job: &'a str,
|
||||
stage: u32,
|
||||
target: &'a str,
|
||||
}
|
||||
|
||||
// We have to use a template for the TestGroup instead of a macro, because
|
||||
// macros cannot be recursive in askama at the moment.
|
||||
#[derive(Template)]
|
||||
#[template(path = "test_group.askama")]
|
||||
/// Represents a group of tests
|
||||
struct TestGroup<'a> {
|
||||
name: String,
|
||||
/// Tests located directly in this directory
|
||||
root_tests: Vec<(String, Test<'a>)>,
|
||||
/// Nested directories with additional tests
|
||||
groups: Vec<(String, TestGroup<'a>)>,
|
||||
}
|
||||
|
||||
impl<'a> TestGroup<'a> {
|
||||
fn test_count(&self) -> u64 {
|
||||
let root = self.root_tests.len() as u64;
|
||||
self.groups.iter().map(|(_, group)| group.test_count()).sum::<u64>() + root
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "test_suites.askama")]
|
||||
struct TestSuitesPage<'a> {
|
||||
suites: TestSuites<'a>,
|
||||
test_count: u64,
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Context;
|
||||
|
@ -28,3 +29,8 @@ where
|
|||
func();
|
||||
println!("</details>\n");
|
||||
}
|
||||
|
||||
/// Normalizes Windows-style path delimiters to Unix-style paths.
|
||||
pub fn normalize_path_delimiters(name: &str) -> Cow<str> {
|
||||
if name.contains("\\") { name.replace('\\', "/").into() } else { name.into() }
|
||||
}
|
||||
|
|
22
src/ci/citool/templates/layout.askama
Normal file
22
src/ci/citool/templates/layout.askama
Normal file
|
@ -0,0 +1,22 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Rust CI Test Dashboard</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
max-width: 1500px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
{% block styles %}{% endblock %}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% block content %}{% endblock %}
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
42
src/ci/citool/templates/test_group.askama
Normal file
42
src/ci/citool/templates/test_group.askama
Normal file
|
@ -0,0 +1,42 @@
|
|||
{% macro test_result(r) -%}
|
||||
passed: {{ r.passed.len() }}, ignored: {{ r.ignored.len() }}
|
||||
{%- endmacro %}
|
||||
|
||||
<li>
|
||||
<details>
|
||||
<summary>{{ name }} ({{ test_count() }} test{{ test_count() | pluralize }}{% if !root_tests.is_empty() && root_tests.len() as u64 != test_count() -%}
|
||||
, {{ root_tests.len() }} root test{{ root_tests.len() | pluralize }}
|
||||
{%- endif %}{% if !groups.is_empty() -%}
|
||||
, {{ groups.len() }} subdir{{ groups.len() | pluralize }}
|
||||
{%- endif %})
|
||||
</summary>
|
||||
|
||||
{% if !groups.is_empty() %}
|
||||
<ul>
|
||||
{% for (dir_name, subgroup) in groups %}
|
||||
{{ subgroup|safe }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if !root_tests.is_empty() %}
|
||||
<ul>
|
||||
{% for (name, test) in root_tests %}
|
||||
<li>
|
||||
{% if let Some(result) = test.single_test() %}
|
||||
<b>{{ name }}</b> ({% call test_result(result) %})
|
||||
{% else %}
|
||||
<b>{{ name }}</b> ({{ test.revisions.len() }} revision{{ test.revisions.len() | pluralize }})
|
||||
<ul>
|
||||
{% for (revision, result) in test.revisions %}
|
||||
<li>#<i>{{ revision }}</i> ({% call test_result(result) %})</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
</details>
|
||||
</li>
|
108
src/ci/citool/templates/test_suites.askama
Normal file
108
src/ci/citool/templates/test_suites.askama
Normal file
|
@ -0,0 +1,108 @@
|
|||
{% extends "layout.askama" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Rust CI test dashboard</h1>
|
||||
<div>
|
||||
Here's how to interpret the "passed" and "ignored" counts:
|
||||
the count includes all combinations of "stage" x "target" x "CI job where the test was executed or ignored".
|
||||
</div>
|
||||
<div class="test-suites">
|
||||
<div class="summary">
|
||||
<div>
|
||||
<div class="test-count">Total tests: {{ test_count }}</div>
|
||||
<div>
|
||||
To find tests that haven't been executed anywhere, click on "Open all" and search for "passed: 0".
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button onclick="openAll()">Open all</button>
|
||||
<button onclick="closeAll()">Close all</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
{% for suite in suites.suites %}
|
||||
{{ suite.group|safe }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #333333;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.summary {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.test-count {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.test-suites {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
padding-left: 20px;
|
||||
}
|
||||
summary {
|
||||
margin-bottom: 5px;
|
||||
padding: 6px;
|
||||
background-color: #F4F4F4;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
summary:hover {
|
||||
background-color: #CFCFCF;
|
||||
}
|
||||
|
||||
/* Style the disclosure triangles */
|
||||
details > summary {
|
||||
list-style: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
details > summary::before {
|
||||
content: "▶";
|
||||
position: absolute;
|
||||
left: -15px;
|
||||
transform: rotate(0);
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
details[open] > summary::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script type="text/javascript">
|
||||
function openAll() {
|
||||
const details = document.getElementsByTagName("details");
|
||||
for (const elem of details) {
|
||||
elem.open = true;
|
||||
}
|
||||
}
|
||||
function closeAll() {
|
||||
const details = document.getElementsByTagName("details");
|
||||
for (const elem of details) {
|
||||
elem.open = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -20,6 +20,43 @@ your `.git/hooks` folder as `pre-push` (without the `.sh` extension!).
|
|||
|
||||
You can also install the hook as a step of running `./x setup`!
|
||||
|
||||
## Config extensions
|
||||
|
||||
When working on different tasks, you might need to switch between different bootstrap configurations.
|
||||
Sometimes you may want to keep an old configuration for future use. But saving raw config values in
|
||||
random files and manually copying and pasting them can quickly become messy, especially if you have a
|
||||
long history of different configurations.
|
||||
|
||||
To simplify managing multiple configurations, you can create config extensions.
|
||||
|
||||
For example, you can create a simple config file named `cross.toml`:
|
||||
|
||||
```toml
|
||||
[build]
|
||||
build = "x86_64-unknown-linux-gnu"
|
||||
host = ["i686-unknown-linux-gnu"]
|
||||
target = ["i686-unknown-linux-gnu"]
|
||||
|
||||
|
||||
[llvm]
|
||||
download-ci-llvm = false
|
||||
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
llvm-config = "/path/to/llvm-19/bin/llvm-config"
|
||||
```
|
||||
|
||||
Then, include this in your `bootstrap.toml`:
|
||||
|
||||
```toml
|
||||
include = ["cross.toml"]
|
||||
```
|
||||
|
||||
You can also include extensions within extensions recursively.
|
||||
|
||||
**Note:** In the `include` field, the overriding logic follows a right-to-left order. For example,
|
||||
in `include = ["a.toml", "b.toml"]`, extension `b.toml` overrides `a.toml`. Also, parent extensions
|
||||
always overrides the inner ones.
|
||||
|
||||
## Configuring `rust-analyzer` for `rustc`
|
||||
|
||||
### Project-local rust-analyzer setup
|
||||
|
|
|
@ -247,18 +247,17 @@ See the [Clang ControlFlowIntegrity documentation][clang-cfi] for more details.
|
|||
```rust,ignore (making doc tests pass cross-platform is hard)
|
||||
#![feature(naked_functions)]
|
||||
|
||||
use std::arch::asm;
|
||||
use std::arch::naked_asm;
|
||||
use std::mem;
|
||||
|
||||
fn add_one(x: i32) -> i32 {
|
||||
x + 1
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn add_two(x: i32) {
|
||||
// x + 2 preceded by a landing pad/nop block
|
||||
unsafe {
|
||||
asm!(
|
||||
naked_asm!(
|
||||
"
|
||||
nop
|
||||
nop
|
||||
|
@ -271,11 +270,9 @@ pub extern "C" fn add_two(x: i32) {
|
|||
nop
|
||||
lea eax, [rdi+2]
|
||||
ret
|
||||
",
|
||||
options(noreturn)
|
||||
"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
|
||||
f(arg) + f(arg)
|
||||
|
|
|
@ -1397,7 +1397,6 @@ ui/issues/auxiliary/issue-13620-1.rs
|
|||
ui/issues/auxiliary/issue-13620-2.rs
|
||||
ui/issues/auxiliary/issue-14344-1.rs
|
||||
ui/issues/auxiliary/issue-14344-2.rs
|
||||
ui/issues/auxiliary/issue-14421.rs
|
||||
ui/issues/auxiliary/issue-14422.rs
|
||||
ui/issues/auxiliary/issue-15562.rs
|
||||
ui/issues/auxiliary/issue-16643.rs
|
||||
|
@ -1564,7 +1563,6 @@ ui/issues/issue-14366.rs
|
|||
ui/issues/issue-14382.rs
|
||||
ui/issues/issue-14393.rs
|
||||
ui/issues/issue-14399.rs
|
||||
ui/issues/issue-14421.rs
|
||||
ui/issues/issue-14422.rs
|
||||
ui/issues/issue-14541.rs
|
||||
ui/issues/issue-14721.rs
|
||||
|
@ -1629,7 +1627,6 @@ ui/issues/issue-16774.rs
|
|||
ui/issues/issue-16783.rs
|
||||
ui/issues/issue-16819.rs
|
||||
ui/issues/issue-16922-rpass.rs
|
||||
ui/issues/issue-16939.rs
|
||||
ui/issues/issue-16966.rs
|
||||
ui/issues/issue-16994.rs
|
||||
ui/issues/issue-17001.rs
|
||||
|
@ -1867,7 +1864,6 @@ ui/issues/issue-23550.rs
|
|||
ui/issues/issue-23589.rs
|
||||
ui/issues/issue-23699.rs
|
||||
ui/issues/issue-2380-b.rs
|
||||
ui/issues/issue-23808.rs
|
||||
ui/issues/issue-2383.rs
|
||||
ui/issues/issue-23891.rs
|
||||
ui/issues/issue-23898.rs
|
||||
|
@ -2607,7 +2603,6 @@ ui/issues/issue-9249.rs
|
|||
ui/issues/issue-9259.rs
|
||||
ui/issues/issue-92741.rs
|
||||
ui/issues/issue-9446.rs
|
||||
ui/issues/issue-9719.rs
|
||||
ui/issues/issue-9725.rs
|
||||
ui/issues/issue-9737.rs
|
||||
ui/issues/issue-9814.rs
|
||||
|
@ -3138,7 +3133,6 @@ ui/nll/user-annotations/issue-55241.rs
|
|||
ui/nll/user-annotations/issue-55748-pat-types-constrain-bindings.rs
|
||||
ui/nll/user-annotations/issue-57731-ascibed-coupled-types.rs
|
||||
ui/numbers-arithmetic/issue-8460.rs
|
||||
ui/on-unimplemented/issue-104140.rs
|
||||
ui/or-patterns/issue-64879-trailing-before-guard.rs
|
||||
ui/or-patterns/issue-67514-irrefutable-param.rs
|
||||
ui/or-patterns/issue-68785-irrefutable-param-with-at.rs
|
||||
|
|
|
@ -17,7 +17,7 @@ use ignore::Walk;
|
|||
const ENTRY_LIMIT: u32 = 901;
|
||||
// FIXME: The following limits should be reduced eventually.
|
||||
|
||||
const ISSUES_ENTRY_LIMIT: u32 = 1631;
|
||||
const ISSUES_ENTRY_LIMIT: u32 = 1626;
|
||||
|
||||
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
|
||||
"rs", // test source files
|
||||
|
|
|
@ -13,8 +13,8 @@ use std::arch::naked_asm;
|
|||
// LLVM implements this via making sure of that, even for functions with the naked attribute.
|
||||
// So, we must emit an appropriate instruction instead!
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
pub unsafe extern "C" fn _hlt() -> ! {
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn _hlt() -> ! {
|
||||
// CHECK-NOT: hint #34
|
||||
// CHECK: hlt #0x1
|
||||
naked_asm!("hlt #1")
|
||||
|
|
|
@ -29,7 +29,7 @@ use minicore::*;
|
|||
// CHECK-LABEL: blr:
|
||||
// CHECK: blr
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn blr() {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn blr() {
|
||||
naked_asm!("blr")
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ use minicore::*;
|
|||
// CHECK-NOT: .size
|
||||
// CHECK: end_function
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn nop() {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn nop() {
|
||||
naked_asm!("nop")
|
||||
}
|
||||
|
||||
|
@ -34,11 +34,11 @@ unsafe extern "C" fn nop() {
|
|||
// CHECK-NOT: .size
|
||||
// CHECK: end_function
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[linkage = "weak"]
|
||||
// wasm functions cannot be aligned, so this has no effect
|
||||
#[repr(align(32))]
|
||||
unsafe extern "C" fn weak_aligned_nop() {
|
||||
extern "C" fn weak_aligned_nop() {
|
||||
naked_asm!("nop")
|
||||
}
|
||||
|
||||
|
@ -51,48 +51,48 @@ unsafe extern "C" fn weak_aligned_nop() {
|
|||
//
|
||||
// CHECK-NEXT: end_function
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn fn_i8_i8(num: i8) -> i8 {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn fn_i8_i8(num: i8) -> i8 {
|
||||
naked_asm!("local.get 0", "local.get 0", "i32.mul")
|
||||
}
|
||||
|
||||
// CHECK-LABEL: fn_i8_i8_i8:
|
||||
// CHECK: .functype fn_i8_i8_i8 (i32, i32) -> (i32)
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn fn_i8_i8_i8(a: i8, b: i8) -> i8 {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn fn_i8_i8_i8(a: i8, b: i8) -> i8 {
|
||||
naked_asm!("local.get 1", "local.get 0", "i32.mul")
|
||||
}
|
||||
|
||||
// CHECK-LABEL: fn_unit_i8:
|
||||
// CHECK: .functype fn_unit_i8 () -> (i32)
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn fn_unit_i8() -> i8 {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn fn_unit_i8() -> i8 {
|
||||
naked_asm!("i32.const 42")
|
||||
}
|
||||
|
||||
// CHECK-LABEL: fn_i8_unit:
|
||||
// CHECK: .functype fn_i8_unit (i32) -> ()
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn fn_i8_unit(_: i8) {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn fn_i8_unit(_: i8) {
|
||||
naked_asm!("nop")
|
||||
}
|
||||
|
||||
// CHECK-LABEL: fn_i32_i32:
|
||||
// CHECK: .functype fn_i32_i32 (i32) -> (i32)
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn fn_i32_i32(num: i32) -> i32 {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn fn_i32_i32(num: i32) -> i32 {
|
||||
naked_asm!("local.get 0", "local.get 0", "i32.mul")
|
||||
}
|
||||
|
||||
// CHECK-LABEL: fn_i64_i64:
|
||||
// CHECK: .functype fn_i64_i64 (i64) -> (i64)
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn fn_i64_i64(num: i64) -> i64 {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn fn_i64_i64(num: i64) -> i64 {
|
||||
naked_asm!("local.get 0", "local.get 0", "i64.mul")
|
||||
}
|
||||
|
||||
|
@ -101,8 +101,8 @@ unsafe extern "C" fn fn_i64_i64(num: i64) -> i64 {
|
|||
// wasm64-unknown: .functype fn_i128_i128 (i64, i64, i64) -> ()
|
||||
#[allow(improper_ctypes_definitions)]
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn fn_i128_i128(num: i128) -> i128 {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn fn_i128_i128(num: i128) -> i128 {
|
||||
naked_asm!(
|
||||
"local.get 0",
|
||||
"local.get 2",
|
||||
|
@ -117,8 +117,8 @@ unsafe extern "C" fn fn_i128_i128(num: i128) -> i128 {
|
|||
// wasm32-wasip1: .functype fn_f128_f128 (i32, i64, i64) -> ()
|
||||
// wasm64-unknown: .functype fn_f128_f128 (i64, i64, i64) -> ()
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn fn_f128_f128(num: f128) -> f128 {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn fn_f128_f128(num: f128) -> f128 {
|
||||
naked_asm!(
|
||||
"local.get 0",
|
||||
"local.get 2",
|
||||
|
@ -139,8 +139,8 @@ struct Compound {
|
|||
// wasm32-wasip1: .functype fn_compound_compound (i32, i32) -> ()
|
||||
// wasm64-unknown: .functype fn_compound_compound (i64, i64) -> ()
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn fn_compound_compound(_: Compound) -> Compound {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn fn_compound_compound(_: Compound) -> Compound {
|
||||
// this is the wasm32-wasip1 assembly
|
||||
naked_asm!(
|
||||
"local.get 0",
|
||||
|
@ -160,8 +160,8 @@ struct WrapperI32(i32);
|
|||
// CHECK-LABEL: fn_wrapperi32_wrapperi32:
|
||||
// CHECK: .functype fn_wrapperi32_wrapperi32 (i32) -> (i32)
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn fn_wrapperi32_wrapperi32(_: WrapperI32) -> WrapperI32 {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn fn_wrapperi32_wrapperi32(_: WrapperI32) -> WrapperI32 {
|
||||
naked_asm!("local.get 0")
|
||||
}
|
||||
|
||||
|
@ -171,8 +171,8 @@ struct WrapperI64(i64);
|
|||
// CHECK-LABEL: fn_wrapperi64_wrapperi64:
|
||||
// CHECK: .functype fn_wrapperi64_wrapperi64 (i64) -> (i64)
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn fn_wrapperi64_wrapperi64(_: WrapperI64) -> WrapperI64 {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn fn_wrapperi64_wrapperi64(_: WrapperI64) -> WrapperI64 {
|
||||
naked_asm!("local.get 0")
|
||||
}
|
||||
|
||||
|
@ -182,8 +182,8 @@ struct WrapperF32(f32);
|
|||
// CHECK-LABEL: fn_wrapperf32_wrapperf32:
|
||||
// CHECK: .functype fn_wrapperf32_wrapperf32 (f32) -> (f32)
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn fn_wrapperf32_wrapperf32(_: WrapperF32) -> WrapperF32 {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn fn_wrapperf32_wrapperf32(_: WrapperF32) -> WrapperF32 {
|
||||
naked_asm!("local.get 0")
|
||||
}
|
||||
|
||||
|
@ -193,7 +193,7 @@ struct WrapperF64(f64);
|
|||
// CHECK-LABEL: fn_wrapperf64_wrapperf64:
|
||||
// CHECK: .functype fn_wrapperf64_wrapperf64 (f64) -> (f64)
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn fn_wrapperf64_wrapperf64(_: WrapperF64) -> WrapperF64 {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn fn_wrapperf64_wrapperf64(_: WrapperF64) -> WrapperF64 {
|
||||
naked_asm!("local.get 0")
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ use std::arch::naked_asm;
|
|||
// works by using an instruction for each possible landing site,
|
||||
// and LLVM implements this via making sure of that.
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
pub unsafe extern "sysv64" fn will_halt() -> ! {
|
||||
#[unsafe(naked)]
|
||||
pub extern "sysv64" fn will_halt() -> ! {
|
||||
// CHECK-NOT: endbr{{32|64}}
|
||||
// CHECK: hlt
|
||||
naked_asm!("hlt")
|
||||
|
|
|
@ -8,11 +8,9 @@
|
|||
#![feature(naked_functions)]
|
||||
#![no_std]
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn c_variadic(_: usize, _: ...) {
|
||||
// CHECK-NOT: va_start
|
||||
// CHECK-NOT: alloca
|
||||
core::arch::naked_asm! {
|
||||
"ret",
|
||||
}
|
||||
core::arch::naked_asm!("ret")
|
||||
}
|
||||
|
|
|
@ -13,10 +13,10 @@ pub fn caller() {
|
|||
}
|
||||
|
||||
// CHECK: declare x86_intrcc void @page_fault_handler(ptr {{.*}}, i64{{.*}}){{.*}}#[[ATTRS:[0-9]+]]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[no_mangle]
|
||||
pub extern "x86-interrupt" fn page_fault_handler(_: u64, _: u64) {
|
||||
unsafe { core::arch::naked_asm!("ud2") };
|
||||
core::arch::naked_asm!("ud2")
|
||||
}
|
||||
|
||||
// CHECK: #[[ATTRS]] =
|
||||
|
|
|
@ -10,8 +10,8 @@ use std::arch::naked_asm;
|
|||
// CHECK-LABEL: naked_empty:
|
||||
#[repr(align(16))]
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
pub unsafe extern "C" fn naked_empty() {
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn naked_empty() {
|
||||
// CHECK: ret
|
||||
naked_asm!("ret");
|
||||
naked_asm!("ret")
|
||||
}
|
||||
|
|
|
@ -28,11 +28,10 @@ fn test(x: u64) {
|
|||
// CHECK: add rax, 1
|
||||
// CHECK: add rax, 42
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn using_const_generics<const N: u64>(x: u64) -> u64 {
|
||||
const M: u64 = 42;
|
||||
|
||||
unsafe {
|
||||
naked_asm!(
|
||||
"xor rax, rax",
|
||||
"add rax, rdi",
|
||||
|
@ -43,7 +42,6 @@ pub extern "C" fn using_const_generics<const N: u64>(x: u64) -> u64 {
|
|||
const M,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
trait Invert {
|
||||
fn invert(self) -> Self;
|
||||
|
@ -60,17 +58,15 @@ impl Invert for i64 {
|
|||
// CHECK: call
|
||||
// CHECK: ret
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn generic_function<T: Invert>(x: i64) -> i64 {
|
||||
unsafe {
|
||||
naked_asm!(
|
||||
"call {}",
|
||||
"ret",
|
||||
sym <T as Invert>::invert,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
|
@ -81,10 +77,10 @@ struct Foo(u64);
|
|||
// CHECK: mov rax, rdi
|
||||
|
||||
impl Foo {
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[no_mangle]
|
||||
extern "C" fn method(self) -> u64 {
|
||||
unsafe { naked_asm!("mov rax, rdi", "ret") }
|
||||
naked_asm!("mov rax, rdi", "ret")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,10 +93,10 @@ trait Bar {
|
|||
}
|
||||
|
||||
impl Bar for Foo {
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[no_mangle]
|
||||
extern "C" fn trait_method(self) -> u64 {
|
||||
unsafe { naked_asm!("mov rax, rdi", "ret") }
|
||||
naked_asm!("mov rax, rdi", "ret")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +105,7 @@ impl Bar for Foo {
|
|||
// CHECK: lea rax, [rdi + rsi]
|
||||
|
||||
// this previously ICE'd, see https://github.com/rust-lang/rust/issues/124375
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize {
|
||||
naked_asm!("lea rax, [rdi + rsi]", "ret");
|
||||
|
|
|
@ -20,8 +20,8 @@ use minicore::*;
|
|||
// arm-mode: .arm
|
||||
// thumb-mode: .thumb
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn test_unspecified() {
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn test_unspecified() {
|
||||
naked_asm!("bx lr");
|
||||
}
|
||||
|
||||
|
@ -33,9 +33,9 @@ unsafe extern "C" fn test_unspecified() {
|
|||
// arm-mode: .arm
|
||||
// thumb-mode: .thumb
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[instruction_set(arm::t32)]
|
||||
unsafe extern "C" fn test_thumb() {
|
||||
extern "C" fn test_thumb() {
|
||||
naked_asm!("bx lr");
|
||||
}
|
||||
|
||||
|
@ -46,8 +46,8 @@ unsafe extern "C" fn test_thumb() {
|
|||
// arm-mode: .arm
|
||||
// thumb-mode: .thumb
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[instruction_set(arm::a32)]
|
||||
unsafe extern "C" fn test_arm() {
|
||||
extern "C" fn test_arm() {
|
||||
naked_asm!("bx lr");
|
||||
}
|
||||
|
|
|
@ -9,24 +9,24 @@
|
|||
//
|
||||
// CHECK: .balign 16
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
pub unsafe extern "C" fn naked_no_explicit_align() {
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn naked_no_explicit_align() {
|
||||
core::arch::naked_asm!("ret")
|
||||
}
|
||||
|
||||
// CHECK: .balign 16
|
||||
#[no_mangle]
|
||||
#[repr(align(8))]
|
||||
#[naked]
|
||||
pub unsafe extern "C" fn naked_lower_align() {
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn naked_lower_align() {
|
||||
core::arch::naked_asm!("ret")
|
||||
}
|
||||
|
||||
// CHECK: .balign 32
|
||||
#[no_mangle]
|
||||
#[repr(align(32))]
|
||||
#[naked]
|
||||
pub unsafe extern "C" fn naked_higher_align() {
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn naked_higher_align() {
|
||||
core::arch::naked_asm!("ret")
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ pub unsafe extern "C" fn naked_higher_align() {
|
|||
// CHECK: .balign 16
|
||||
#[no_mangle]
|
||||
#[cold]
|
||||
#[naked]
|
||||
pub unsafe extern "C" fn no_explicit_align_cold() {
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn no_explicit_align_cold() {
|
||||
core::arch::naked_asm!("ret")
|
||||
}
|
||||
|
|
|
@ -60,8 +60,8 @@ use minicore::*;
|
|||
// linux,win: .att_syntax
|
||||
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
pub unsafe extern "C" fn naked_empty() {
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn naked_empty() {
|
||||
#[cfg(not(all(target_arch = "arm", target_feature = "thumb-mode")))]
|
||||
naked_asm!("ret");
|
||||
|
||||
|
@ -114,8 +114,8 @@ pub unsafe extern "C" fn naked_empty() {
|
|||
// linux,win: .att_syntax
|
||||
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize {
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
{
|
||||
naked_asm!("lea rax, [rdi + rsi]", "ret")
|
||||
|
@ -138,9 +138,9 @@ pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize
|
|||
// thumb: .pushsection .text.some_different_name,\22ax\22, %progbits
|
||||
// CHECK-LABEL: test_link_section:
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[link_section = ".text.some_different_name"]
|
||||
pub unsafe extern "C" fn test_link_section() {
|
||||
pub extern "C" fn test_link_section() {
|
||||
#[cfg(not(all(target_arch = "arm", target_feature = "thumb-mode")))]
|
||||
naked_asm!("ret");
|
||||
|
||||
|
@ -159,7 +159,7 @@ pub unsafe extern "C" fn test_link_section() {
|
|||
// win_i686-LABEL: @fastcall_cc@4:
|
||||
#[cfg(target_os = "windows")]
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
pub unsafe extern "fastcall" fn fastcall_cc(x: i32) -> i32 {
|
||||
#[unsafe(naked)]
|
||||
pub extern "fastcall" fn fastcall_cc(x: i32) -> i32 {
|
||||
naked_asm!("ret");
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
//@ known-bug: #130627
|
||||
|
||||
#![feature(trait_alias)]
|
||||
|
||||
trait Test {}
|
||||
|
||||
#[diagnostic::on_unimplemented(
|
||||
message="message",
|
||||
label="label",
|
||||
note="note"
|
||||
)]
|
||||
trait Alias = Test;
|
||||
|
||||
// Use trait alias as bound on type parameter.
|
||||
fn foo<T: Alias>(v: &T) {
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
foo(&1);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#![crate_name = "crateresolve1"]
|
||||
#![crate_type = "lib"]
|
||||
|
||||
pub fn f() -> isize {
|
||||
10
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#![crate_name = "crateresolve1"]
|
||||
#![crate_type = "lib"]
|
||||
|
||||
pub fn f() -> isize {
|
||||
20
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
extern crate crateresolve1;
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,12 @@
|
|||
error[E0464]: multiple candidates for `rlib` dependency `crateresolve1` found
|
||||
--> multiple-candidates.rs:1:1
|
||||
|
|
||||
LL | extern crate crateresolve1;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: candidate #1: ./mylibs/libcrateresolve1-1.rlib
|
||||
= note: candidate #2: ./mylibs/libcrateresolve1-2.rlib
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0464`.
|
34
tests/run-make/crate-loading-multiple-candidates/rmake.rs
Normal file
34
tests/run-make/crate-loading-multiple-candidates/rmake.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
//@ needs-symlink
|
||||
//@ ignore-cross-compile
|
||||
|
||||
// Tests that the multiple candidate dependencies diagnostic prints relative
|
||||
// paths if a relative library path was passed in.
|
||||
|
||||
use run_make_support::{bare_rustc, diff, rfs, rustc};
|
||||
|
||||
fn main() {
|
||||
// Check that relative paths are preserved in the diagnostic
|
||||
rfs::create_dir("mylibs");
|
||||
rustc().input("crateresolve1-1.rs").out_dir("mylibs").extra_filename("-1").run();
|
||||
rustc().input("crateresolve1-2.rs").out_dir("mylibs").extra_filename("-2").run();
|
||||
check("./mylibs");
|
||||
|
||||
// Check that symlinks aren't followed when printing the diagnostic
|
||||
rfs::rename("mylibs", "original");
|
||||
rfs::symlink_dir("original", "mylibs");
|
||||
check("./mylibs");
|
||||
}
|
||||
|
||||
fn check(library_path: &str) {
|
||||
let out = rustc()
|
||||
.input("multiple-candidates.rs")
|
||||
.library_search_path(library_path)
|
||||
.ui_testing()
|
||||
.run_fail()
|
||||
.stderr_utf8();
|
||||
diff()
|
||||
.expected_file("multiple-candidates.stderr")
|
||||
.normalize(r"\\", "/")
|
||||
.actual_text("(rustc)", &out)
|
||||
.run();
|
||||
}
|
|
@ -26,9 +26,9 @@ extern "C" fn private_vanilla() -> u32 {
|
|||
42
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn private_naked() -> u32 {
|
||||
unsafe { naked_asm!("mov rax, 42", "ret") }
|
||||
naked_asm!("mov rax, 42", "ret")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -36,19 +36,19 @@ pub extern "C" fn public_vanilla() -> u32 {
|
|||
42
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn public_naked_nongeneric() -> u32 {
|
||||
unsafe { naked_asm!("mov rax, 42", "ret") }
|
||||
naked_asm!("mov rax, 42", "ret")
|
||||
}
|
||||
|
||||
pub extern "C" fn public_vanilla_generic<T: TraitWithConst>() -> u32 {
|
||||
T::COUNT
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn public_naked_generic<T: TraitWithConst>() -> u32 {
|
||||
unsafe { naked_asm!("mov rax, {}", "ret", const T::COUNT) }
|
||||
naked_asm!("mov rax, {}", "ret", const T::COUNT)
|
||||
}
|
||||
|
||||
#[linkage = "external"]
|
||||
|
@ -56,10 +56,10 @@ extern "C" fn vanilla_external_linkage() -> u32 {
|
|||
42
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[linkage = "external"]
|
||||
extern "C" fn naked_external_linkage() -> u32 {
|
||||
unsafe { naked_asm!("mov rax, 42", "ret") }
|
||||
naked_asm!("mov rax, 42", "ret")
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
|
@ -68,11 +68,11 @@ extern "C" fn vanilla_weak_linkage() -> u32 {
|
|||
42
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[cfg(not(windows))]
|
||||
#[linkage = "weak"]
|
||||
extern "C" fn naked_weak_linkage() -> u32 {
|
||||
unsafe { naked_asm!("mov rax, 42", "ret") }
|
||||
naked_asm!("mov rax, 42", "ret")
|
||||
}
|
||||
|
||||
// functions that are declared in an `extern "C"` block are currently not exported
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
//! Checks variations of E0057, which is the incorrect number of agruments passed into a closure
|
||||
|
||||
//@ check-fail
|
||||
|
||||
fn foo<T: Fn()>(t: T) {
|
||||
t(1i32);
|
||||
//~^ ERROR function takes 0 arguments but 1 argument was supplied
|
||||
}
|
||||
|
||||
/// Regression test for <https://github.com/rust-lang/rust/issues/16939>
|
||||
fn foo2<T: Fn()>(f: T) {
|
||||
|t| f(t);
|
||||
//~^ ERROR function takes 0 arguments but 1 argument was supplied
|
||||
}
|
||||
|
||||
fn bar(t: impl Fn()) {
|
||||
t(1i32);
|
||||
//~^ ERROR function takes 0 arguments but 1 argument was supplied
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
error[E0057]: this function takes 0 arguments but 1 argument was supplied
|
||||
--> $DIR/exotic-calls.rs:2:5
|
||||
--> $DIR/exotic-calls.rs:6:5
|
||||
|
|
||||
LL | t(1i32);
|
||||
| ^ ---- unexpected argument of type `i32`
|
||||
|
|
||||
note: callable defined here
|
||||
--> $DIR/exotic-calls.rs:1:11
|
||||
--> $DIR/exotic-calls.rs:5:11
|
||||
|
|
||||
LL | fn foo<T: Fn()>(t: T) {
|
||||
| ^^^^
|
||||
|
@ -16,13 +16,30 @@ LL + t();
|
|||
|
|
||||
|
||||
error[E0057]: this function takes 0 arguments but 1 argument was supplied
|
||||
--> $DIR/exotic-calls.rs:7:5
|
||||
--> $DIR/exotic-calls.rs:12:9
|
||||
|
|
||||
LL | |t| f(t);
|
||||
| ^ - unexpected argument
|
||||
|
|
||||
note: callable defined here
|
||||
--> $DIR/exotic-calls.rs:11:12
|
||||
|
|
||||
LL | fn foo2<T: Fn()>(f: T) {
|
||||
| ^^^^
|
||||
help: remove the extra argument
|
||||
|
|
||||
LL - |t| f(t);
|
||||
LL + |t| f();
|
||||
|
|
||||
|
||||
error[E0057]: this function takes 0 arguments but 1 argument was supplied
|
||||
--> $DIR/exotic-calls.rs:17:5
|
||||
|
|
||||
LL | t(1i32);
|
||||
| ^ ---- unexpected argument of type `i32`
|
||||
|
|
||||
note: type parameter defined here
|
||||
--> $DIR/exotic-calls.rs:6:11
|
||||
--> $DIR/exotic-calls.rs:16:11
|
||||
|
|
||||
LL | fn bar(t: impl Fn()) {
|
||||
| ^^^^^^^^^
|
||||
|
@ -33,13 +50,13 @@ LL + t();
|
|||
|
|
||||
|
||||
error[E0057]: this function takes 0 arguments but 1 argument was supplied
|
||||
--> $DIR/exotic-calls.rs:16:5
|
||||
--> $DIR/exotic-calls.rs:26:5
|
||||
|
|
||||
LL | baz()(1i32)
|
||||
| ^^^^^ ---- unexpected argument of type `i32`
|
||||
|
|
||||
note: opaque type defined here
|
||||
--> $DIR/exotic-calls.rs:11:13
|
||||
--> $DIR/exotic-calls.rs:21:13
|
||||
|
|
||||
LL | fn baz() -> impl Fn() {
|
||||
| ^^^^^^^^^
|
||||
|
@ -50,13 +67,13 @@ LL + baz()()
|
|||
|
|
||||
|
||||
error[E0057]: this function takes 0 arguments but 1 argument was supplied
|
||||
--> $DIR/exotic-calls.rs:22:5
|
||||
--> $DIR/exotic-calls.rs:32:5
|
||||
|
|
||||
LL | x(1i32);
|
||||
| ^ ---- unexpected argument of type `i32`
|
||||
|
|
||||
note: closure defined here
|
||||
--> $DIR/exotic-calls.rs:21:13
|
||||
--> $DIR/exotic-calls.rs:31:13
|
||||
|
|
||||
LL | let x = || {};
|
||||
| ^^
|
||||
|
@ -66,6 +83,6 @@ LL - x(1i32);
|
|||
LL + x();
|
||||
|
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0057`.
|
||||
|
|
|
@ -12,24 +12,24 @@ fn main() {
|
|||
test1();
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn test1() {
|
||||
unsafe { naked_asm!("") }
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
extern "C" fn test2() {
|
||||
unsafe { naked_asm!("") }
|
||||
//~^ ERROR the `naked_asm!` macro can only be used in functions marked with `#[naked]`
|
||||
naked_asm!("")
|
||||
//~^ ERROR the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]`
|
||||
}
|
||||
|
||||
extern "C" fn test3() {
|
||||
unsafe { (|| naked_asm!(""))() }
|
||||
//~^ ERROR the `naked_asm!` macro can only be used in functions marked with `#[naked]`
|
||||
(|| naked_asm!(""))()
|
||||
//~^ ERROR the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]`
|
||||
}
|
||||
|
||||
fn test4() {
|
||||
async move {
|
||||
unsafe { naked_asm!("") } ;
|
||||
//~^ ERROR the `naked_asm!` macro can only be used in functions marked with `#[naked]`
|
||||
naked_asm!("");
|
||||
//~^ ERROR the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]`
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
error: the `naked_asm!` macro can only be used in functions marked with `#[naked]`
|
||||
--> $DIR/naked-asm-outside-naked-fn.rs:21:14
|
||||
error: the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]`
|
||||
--> $DIR/naked-asm-outside-naked-fn.rs:21:5
|
||||
|
|
||||
LL | unsafe { naked_asm!("") }
|
||||
LL | naked_asm!("")
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: the `naked_asm!` macro can only be used in functions marked with `#[naked]`
|
||||
--> $DIR/naked-asm-outside-naked-fn.rs:26:18
|
||||
error: the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]`
|
||||
--> $DIR/naked-asm-outside-naked-fn.rs:26:9
|
||||
|
|
||||
LL | unsafe { (|| naked_asm!(""))() }
|
||||
LL | (|| naked_asm!(""))()
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: the `naked_asm!` macro can only be used in functions marked with `#[naked]`
|
||||
--> $DIR/naked-asm-outside-naked-fn.rs:32:19
|
||||
error: the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]`
|
||||
--> $DIR/naked-asm-outside-naked-fn.rs:32:9
|
||||
|
|
||||
LL | unsafe { naked_asm!("") } ;
|
||||
LL | naked_asm!("");
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
|
|
@ -5,11 +5,9 @@
|
|||
|
||||
use std::arch::naked_asm;
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn naked(p: char) -> u128 {
|
||||
//~^ WARN uses type `char`
|
||||
//~| WARN uses type `u128`
|
||||
unsafe {
|
||||
naked_asm!("");
|
||||
}
|
||||
naked_asm!("")
|
||||
}
|
||||
|
|
|
@ -4,35 +4,35 @@
|
|||
|
||||
use std::arch::naked_asm;
|
||||
|
||||
#[naked]
|
||||
pub unsafe extern "C" fn inline_none() {
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn inline_none() {
|
||||
naked_asm!("");
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[inline]
|
||||
//~^ ERROR [E0736]
|
||||
pub unsafe extern "C" fn inline_hint() {
|
||||
pub extern "C" fn inline_hint() {
|
||||
naked_asm!("");
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[inline(always)]
|
||||
//~^ ERROR [E0736]
|
||||
pub unsafe extern "C" fn inline_always() {
|
||||
pub extern "C" fn inline_always() {
|
||||
naked_asm!("");
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[inline(never)]
|
||||
//~^ ERROR [E0736]
|
||||
pub unsafe extern "C" fn inline_never() {
|
||||
pub extern "C" fn inline_never() {
|
||||
naked_asm!("");
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[cfg_attr(all(), inline(never))]
|
||||
//~^ ERROR [E0736]
|
||||
pub unsafe extern "C" fn conditional_inline_never() {
|
||||
pub extern "C" fn conditional_inline_never() {
|
||||
naked_asm!("");
|
||||
}
|
||||
|
|
|
@ -1,34 +1,34 @@
|
|||
error[E0736]: attribute incompatible with `#[naked]`
|
||||
error[E0736]: attribute incompatible with `#[unsafe(naked)]`
|
||||
--> $DIR/naked-functions-inline.rs:13:1
|
||||
|
|
||||
LL | #[naked]
|
||||
| -------- function marked with `#[naked]` here
|
||||
LL | #[unsafe(naked)]
|
||||
| ---------------- function marked with `#[unsafe(naked)]` here
|
||||
LL | #[inline]
|
||||
| ^^^^^^^^^ the `inline` attribute is incompatible with `#[naked]`
|
||||
| ^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]`
|
||||
|
||||
error[E0736]: attribute incompatible with `#[naked]`
|
||||
error[E0736]: attribute incompatible with `#[unsafe(naked)]`
|
||||
--> $DIR/naked-functions-inline.rs:20:1
|
||||
|
|
||||
LL | #[naked]
|
||||
| -------- function marked with `#[naked]` here
|
||||
LL | #[unsafe(naked)]
|
||||
| ---------------- function marked with `#[unsafe(naked)]` here
|
||||
LL | #[inline(always)]
|
||||
| ^^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[naked]`
|
||||
| ^^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]`
|
||||
|
||||
error[E0736]: attribute incompatible with `#[naked]`
|
||||
error[E0736]: attribute incompatible with `#[unsafe(naked)]`
|
||||
--> $DIR/naked-functions-inline.rs:27:1
|
||||
|
|
||||
LL | #[naked]
|
||||
| -------- function marked with `#[naked]` here
|
||||
LL | #[unsafe(naked)]
|
||||
| ---------------- function marked with `#[unsafe(naked)]` here
|
||||
LL | #[inline(never)]
|
||||
| ^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[naked]`
|
||||
| ^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]`
|
||||
|
||||
error[E0736]: attribute incompatible with `#[naked]`
|
||||
error[E0736]: attribute incompatible with `#[unsafe(naked)]`
|
||||
--> $DIR/naked-functions-inline.rs:34:19
|
||||
|
|
||||
LL | #[naked]
|
||||
| -------- function marked with `#[naked]` here
|
||||
LL | #[unsafe(naked)]
|
||||
| ---------------- function marked with `#[unsafe(naked)]` here
|
||||
LL | #[cfg_attr(all(), inline(never))]
|
||||
| ^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[naked]`
|
||||
| ^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
|
@ -12,15 +12,15 @@ extern crate minicore;
|
|||
use minicore::*;
|
||||
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[instruction_set(arm::t32)]
|
||||
unsafe extern "C" fn test_thumb() {
|
||||
extern "C" fn test_thumb() {
|
||||
naked_asm!("bx lr");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
#[instruction_set(arm::a32)]
|
||||
unsafe extern "C" fn test_arm() {
|
||||
extern "C" fn test_arm() {
|
||||
naked_asm!("bx lr");
|
||||
}
|
||||
|
|
|
@ -11,17 +11,17 @@
|
|||
|
||||
use std::arch::{asm, naked_asm};
|
||||
|
||||
#[naked]
|
||||
pub unsafe fn rust_implicit() {
|
||||
#[unsafe(naked)]
|
||||
pub fn rust_implicit() {
|
||||
naked_asm!("ret");
|
||||
}
|
||||
|
||||
#[naked]
|
||||
pub unsafe extern "Rust" fn rust_explicit() {
|
||||
#[unsafe(naked)]
|
||||
pub extern "Rust" fn rust_explicit() {
|
||||
naked_asm!("ret");
|
||||
}
|
||||
|
||||
#[naked]
|
||||
pub unsafe extern "rust-cold" fn rust_cold() {
|
||||
#[unsafe(naked)]
|
||||
pub extern "rust-cold" fn rust_cold() {
|
||||
naked_asm!("ret");
|
||||
}
|
||||
|
|
|
@ -8,14 +8,14 @@ use std::arch::{asm, naked_asm};
|
|||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[target_feature(enable = "sse2")]
|
||||
#[naked]
|
||||
pub unsafe extern "C" fn compatible_target_feature() {
|
||||
naked_asm!("");
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn compatible_target_feature() {
|
||||
naked_asm!("ret");
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[target_feature(enable = "neon")]
|
||||
#[naked]
|
||||
pub unsafe extern "C" fn compatible_target_feature() {
|
||||
naked_asm!("");
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn compatible_target_feature() {
|
||||
naked_asm!("ret");
|
||||
}
|
||||
|
|
|
@ -8,31 +8,31 @@
|
|||
use std::arch::naked_asm;
|
||||
|
||||
#[test]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
//~^ ERROR [E0736]
|
||||
extern "C" fn test_naked() {
|
||||
unsafe { naked_asm!("") };
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
//~^ ERROR [E0736]
|
||||
extern "C" fn test_naked_should_panic() {
|
||||
unsafe { naked_asm!("") };
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
//~^ ERROR [E0736]
|
||||
extern "C" fn test_naked_ignore() {
|
||||
unsafe { naked_asm!("") };
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
#[bench]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
//~^ ERROR [E0736]
|
||||
extern "C" fn bench_naked() {
|
||||
unsafe { naked_asm!("") };
|
||||
naked_asm!("")
|
||||
}
|
||||
|
|
|
@ -1,34 +1,34 @@
|
|||
error[E0736]: cannot use `#[naked]` with testing attributes
|
||||
error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes
|
||||
--> $DIR/naked-functions-testattrs.rs:11:1
|
||||
|
|
||||
LL | #[test]
|
||||
| ------- function marked with testing attribute here
|
||||
LL | #[naked]
|
||||
| ^^^^^^^^ `#[naked]` is incompatible with testing attributes
|
||||
LL | #[unsafe(naked)]
|
||||
| ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes
|
||||
|
||||
error[E0736]: cannot use `#[naked]` with testing attributes
|
||||
error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes
|
||||
--> $DIR/naked-functions-testattrs.rs:19:1
|
||||
|
|
||||
LL | #[test]
|
||||
| ------- function marked with testing attribute here
|
||||
LL | #[naked]
|
||||
| ^^^^^^^^ `#[naked]` is incompatible with testing attributes
|
||||
LL | #[unsafe(naked)]
|
||||
| ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes
|
||||
|
||||
error[E0736]: cannot use `#[naked]` with testing attributes
|
||||
error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes
|
||||
--> $DIR/naked-functions-testattrs.rs:27:1
|
||||
|
|
||||
LL | #[test]
|
||||
| ------- function marked with testing attribute here
|
||||
LL | #[naked]
|
||||
| ^^^^^^^^ `#[naked]` is incompatible with testing attributes
|
||||
LL | #[unsafe(naked)]
|
||||
| ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes
|
||||
|
||||
error[E0736]: cannot use `#[naked]` with testing attributes
|
||||
error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes
|
||||
--> $DIR/naked-functions-testattrs.rs:34:1
|
||||
|
|
||||
LL | #[bench]
|
||||
| -------- function marked with testing attribute here
|
||||
LL | #[naked]
|
||||
| ^^^^^^^^ `#[naked]` is incompatible with testing attributes
|
||||
LL | #[unsafe(naked)]
|
||||
| ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
|
@ -64,44 +64,34 @@ pub mod normal {
|
|||
pub mod naked {
|
||||
use std::arch::naked_asm;
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn function(a: usize, b: usize) -> usize {
|
||||
unsafe {
|
||||
naked_asm!("");
|
||||
}
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
pub struct Naked;
|
||||
|
||||
impl Naked {
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn associated(a: usize, b: usize) -> usize {
|
||||
unsafe {
|
||||
naked_asm!("");
|
||||
}
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn method(&self, a: usize, b: usize) -> usize {
|
||||
unsafe {
|
||||
naked_asm!("");
|
||||
}
|
||||
naked_asm!("")
|
||||
}
|
||||
}
|
||||
|
||||
impl super::Trait for Naked {
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn trait_associated(a: usize, b: usize) -> usize {
|
||||
unsafe {
|
||||
naked_asm!("");
|
||||
}
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn trait_method(&self, a: usize, b: usize) -> usize {
|
||||
unsafe {
|
||||
naked_asm!("");
|
||||
}
|
||||
naked_asm!("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
use std::arch::{asm, naked_asm};
|
||||
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn inline_asm_macro() {
|
||||
asm!("", options(raw));
|
||||
pub extern "C" fn inline_asm_macro() {
|
||||
unsafe { asm!("", options(raw)) };
|
||||
//~^ERROR the `asm!` macro is not allowed in naked functions
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ pub struct P {
|
|||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn patterns(
|
||||
pub extern "C" fn patterns(
|
||||
mut a: u32,
|
||||
//~^ ERROR patterns not allowed in naked function parameters
|
||||
&b: &i32,
|
||||
|
@ -35,7 +35,7 @@ pub unsafe extern "C" fn patterns(
|
|||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn inc(a: u32) -> u32 {
|
||||
pub extern "C" fn inc(a: u32) -> u32 {
|
||||
//~^ ERROR naked functions must contain a single `naked_asm!` invocation
|
||||
a + 1
|
||||
//~^ ERROR referencing function parameters is not allowed in naked functions
|
||||
|
@ -43,19 +43,19 @@ pub unsafe extern "C" fn inc(a: u32) -> u32 {
|
|||
|
||||
#[unsafe(naked)]
|
||||
#[allow(asm_sub_register)]
|
||||
pub unsafe extern "C" fn inc_asm(a: u32) -> u32 {
|
||||
pub extern "C" fn inc_asm(a: u32) -> u32 {
|
||||
naked_asm!("/* {0} */", in(reg) a)
|
||||
//~^ ERROR the `in` operand cannot be used with `naked_asm!`
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn inc_closure(a: u32) -> u32 {
|
||||
pub extern "C" fn inc_closure(a: u32) -> u32 {
|
||||
//~^ ERROR naked functions must contain a single `naked_asm!` invocation
|
||||
(|| a + 1)()
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn unsupported_operands() {
|
||||
pub extern "C" fn unsupported_operands() {
|
||||
//~^ ERROR naked functions must contain a single `naked_asm!` invocation
|
||||
let mut a = 0usize;
|
||||
let mut b = 0usize;
|
||||
|
@ -84,12 +84,11 @@ pub extern "C" fn missing_assembly() {
|
|||
#[unsafe(naked)]
|
||||
pub extern "C" fn too_many_asm_blocks() {
|
||||
//~^ ERROR naked functions must contain a single `naked_asm!` invocation
|
||||
unsafe {
|
||||
|
||||
naked_asm!("", options(noreturn));
|
||||
//~^ ERROR the `noreturn` option cannot be used with `naked_asm!`
|
||||
naked_asm!("");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn outer(x: u32) -> extern "C" fn(usize) -> usize {
|
||||
#[unsafe(naked)]
|
||||
|
@ -124,49 +123,44 @@ unsafe extern "C" fn invalid_may_unwind() {
|
|||
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn valid_a<T>() -> T {
|
||||
unsafe {
|
||||
naked_asm!("");
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
pub extern "C" fn valid_b() {
|
||||
unsafe {
|
||||
{
|
||||
{
|
||||
naked_asm!("");
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn valid_c() {
|
||||
pub extern "C" fn valid_c() {
|
||||
naked_asm!("");
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn valid_att_syntax() {
|
||||
pub extern "C" fn valid_att_syntax() {
|
||||
naked_asm!("", options(att_syntax));
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn allow_compile_error(a: u32) -> u32 {
|
||||
pub extern "C" fn allow_compile_error(a: u32) -> u32 {
|
||||
compile_error!("this is a user specified error")
|
||||
//~^ ERROR this is a user specified error
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn allow_compile_error_and_asm(a: u32) -> u32 {
|
||||
pub extern "C" fn allow_compile_error_and_asm(a: u32) -> u32 {
|
||||
compile_error!("this is a user specified error");
|
||||
//~^ ERROR this is a user specified error
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn invalid_asm_syntax(a: u32) -> u32 {
|
||||
pub extern "C" fn invalid_asm_syntax(a: u32) -> u32 {
|
||||
naked_asm!(invalid_syntax)
|
||||
//~^ ERROR asm template must be a string literal
|
||||
}
|
||||
|
@ -174,7 +168,7 @@ pub unsafe extern "C" fn invalid_asm_syntax(a: u32) -> u32 {
|
|||
#[cfg(target_arch = "x86_64")]
|
||||
#[cfg_attr(target_pointer_width = "64", no_mangle)]
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn compatible_cfg_attributes() {
|
||||
pub extern "C" fn compatible_cfg_attributes() {
|
||||
naked_asm!("", options(att_syntax));
|
||||
}
|
||||
|
||||
|
@ -183,20 +177,20 @@ pub unsafe extern "C" fn compatible_cfg_attributes() {
|
|||
#[deny(dead_code)]
|
||||
#[forbid(dead_code)]
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn compatible_diagnostic_attributes() {
|
||||
pub extern "C" fn compatible_diagnostic_attributes() {
|
||||
naked_asm!("", options(raw));
|
||||
}
|
||||
|
||||
#[deprecated = "test"]
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn compatible_deprecated_attributes() {
|
||||
pub extern "C" fn compatible_deprecated_attributes() {
|
||||
naked_asm!("", options(raw));
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[must_use]
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn compatible_must_use_attributes() -> u64 {
|
||||
pub extern "C" fn compatible_must_use_attributes() -> u64 {
|
||||
naked_asm!(
|
||||
"
|
||||
mov rax, 42
|
||||
|
@ -208,13 +202,13 @@ pub unsafe extern "C" fn compatible_must_use_attributes() -> u64 {
|
|||
#[export_name = "exported_function_name"]
|
||||
#[link_section = ".custom_section"]
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn compatible_ffi_attributes_1() {
|
||||
pub extern "C" fn compatible_ffi_attributes_1() {
|
||||
naked_asm!("", options(raw));
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn compatible_codegen_attributes() {
|
||||
pub extern "C" fn compatible_codegen_attributes() {
|
||||
naked_asm!("", options(raw));
|
||||
}
|
||||
|
||||
|
@ -223,12 +217,12 @@ pub unsafe extern "C" fn compatible_codegen_attributes() {
|
|||
// a normal comment
|
||||
#[doc(alias = "ADocAlias")]
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn compatible_doc_attributes() {
|
||||
pub extern "C" fn compatible_doc_attributes() {
|
||||
naked_asm!("", options(raw));
|
||||
}
|
||||
|
||||
#[linkage = "external"]
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn compatible_linkage() {
|
||||
pub extern "C" fn compatible_linkage() {
|
||||
naked_asm!("", options(raw));
|
||||
}
|
||||
|
|
|
@ -11,69 +11,69 @@ LL | in(reg) a,
|
|||
| ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it
|
||||
|
||||
error: the `noreturn` option cannot be used with `naked_asm!`
|
||||
--> $DIR/naked-functions.rs:88:32
|
||||
--> $DIR/naked-functions.rs:88:28
|
||||
|
|
||||
LL | naked_asm!("", options(noreturn));
|
||||
| ^^^^^^^^ the `noreturn` option is not meaningful for global-scoped inline assembly
|
||||
|
||||
error: the `nomem` option cannot be used with `naked_asm!`
|
||||
--> $DIR/naked-functions.rs:106:28
|
||||
--> $DIR/naked-functions.rs:105:28
|
||||
|
|
||||
LL | naked_asm!("", options(nomem, preserves_flags));
|
||||
| ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly
|
||||
|
||||
error: the `preserves_flags` option cannot be used with `naked_asm!`
|
||||
--> $DIR/naked-functions.rs:106:35
|
||||
--> $DIR/naked-functions.rs:105:35
|
||||
|
|
||||
LL | naked_asm!("", options(nomem, preserves_flags));
|
||||
| ^^^^^^^^^^^^^^^ the `preserves_flags` option is not meaningful for global-scoped inline assembly
|
||||
|
||||
error: the `readonly` option cannot be used with `naked_asm!`
|
||||
--> $DIR/naked-functions.rs:113:28
|
||||
--> $DIR/naked-functions.rs:112:28
|
||||
|
|
||||
LL | naked_asm!("", options(readonly, nostack), options(pure));
|
||||
| ^^^^^^^^ the `readonly` option is not meaningful for global-scoped inline assembly
|
||||
|
||||
error: the `nostack` option cannot be used with `naked_asm!`
|
||||
--> $DIR/naked-functions.rs:113:38
|
||||
--> $DIR/naked-functions.rs:112:38
|
||||
|
|
||||
LL | naked_asm!("", options(readonly, nostack), options(pure));
|
||||
| ^^^^^^^ the `nostack` option is not meaningful for global-scoped inline assembly
|
||||
|
||||
error: the `pure` option cannot be used with `naked_asm!`
|
||||
--> $DIR/naked-functions.rs:113:56
|
||||
--> $DIR/naked-functions.rs:112:56
|
||||
|
|
||||
LL | naked_asm!("", options(readonly, nostack), options(pure));
|
||||
| ^^^^ the `pure` option is not meaningful for global-scoped inline assembly
|
||||
|
||||
error: the `may_unwind` option cannot be used with `naked_asm!`
|
||||
--> $DIR/naked-functions.rs:121:28
|
||||
--> $DIR/naked-functions.rs:120:28
|
||||
|
|
||||
LL | naked_asm!("", options(may_unwind));
|
||||
| ^^^^^^^^^^ the `may_unwind` option is not meaningful for global-scoped inline assembly
|
||||
|
||||
error: this is a user specified error
|
||||
--> $DIR/naked-functions.rs:157:5
|
||||
--> $DIR/naked-functions.rs:151:5
|
||||
|
|
||||
LL | compile_error!("this is a user specified error")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this is a user specified error
|
||||
--> $DIR/naked-functions.rs:163:5
|
||||
--> $DIR/naked-functions.rs:157:5
|
||||
|
|
||||
LL | compile_error!("this is a user specified error");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: asm template must be a string literal
|
||||
--> $DIR/naked-functions.rs:170:16
|
||||
--> $DIR/naked-functions.rs:164:16
|
||||
|
|
||||
LL | naked_asm!(invalid_syntax)
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error[E0787]: the `asm!` macro is not allowed in naked functions
|
||||
--> $DIR/naked-functions.rs:13:5
|
||||
--> $DIR/naked-functions.rs:13:14
|
||||
|
|
||||
LL | asm!("", options(raw));
|
||||
LL | unsafe { asm!("", options(raw)) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ consider using the `naked_asm!` macro instead
|
||||
|
||||
error: patterns not allowed in naked function parameters
|
||||
|
@ -111,8 +111,8 @@ LL | a + 1
|
|||
error[E0787]: naked functions must contain a single `naked_asm!` invocation
|
||||
--> $DIR/naked-functions.rs:38:1
|
||||
|
|
||||
LL | pub unsafe extern "C" fn inc(a: u32) -> u32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | pub extern "C" fn inc(a: u32) -> u32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | a + 1
|
||||
| ----- not allowed in naked functions
|
||||
|
@ -120,8 +120,8 @@ LL | a + 1
|
|||
error[E0787]: naked functions must contain a single `naked_asm!` invocation
|
||||
--> $DIR/naked-functions.rs:52:1
|
||||
|
|
||||
LL | pub unsafe extern "C" fn inc_closure(a: u32) -> u32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | pub extern "C" fn inc_closure(a: u32) -> u32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | (|| a + 1)()
|
||||
| ------------ not allowed in naked functions
|
||||
|
@ -129,8 +129,8 @@ LL | (|| a + 1)()
|
|||
error[E0787]: naked functions must contain a single `naked_asm!` invocation
|
||||
--> $DIR/naked-functions.rs:58:1
|
||||
|
|
||||
LL | pub unsafe extern "C" fn unsupported_operands() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | pub extern "C" fn unsupported_operands() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | let mut a = 0usize;
|
||||
| ------------------- not allowed in naked functions
|
||||
|
@ -159,7 +159,7 @@ LL | naked_asm!("");
|
|||
| -------------- multiple `naked_asm!` invocations are not allowed in naked functions
|
||||
|
||||
error: referencing function parameters is not allowed in naked functions
|
||||
--> $DIR/naked-functions.rs:98:11
|
||||
--> $DIR/naked-functions.rs:97:11
|
||||
|
|
||||
LL | *&y
|
||||
| ^
|
||||
|
@ -167,7 +167,7 @@ LL | *&y
|
|||
= help: follow the calling convention in asm block to use parameters
|
||||
|
||||
error[E0787]: naked functions must contain a single `naked_asm!` invocation
|
||||
--> $DIR/naked-functions.rs:96:5
|
||||
--> $DIR/naked-functions.rs:95:5
|
||||
|
|
||||
LL | pub extern "C" fn inner(y: usize) -> usize {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
// Checks that #[naked] attribute can be placed on function definitions only.
|
||||
// Checks that #[unsafe(naked)] attribute can be placed on function definitions only.
|
||||
//
|
||||
//@ needs-asm-support
|
||||
#![feature(naked_functions)]
|
||||
#![naked] //~ ERROR should be applied to a function definition
|
||||
#![unsafe(naked)] //~ ERROR should be applied to a function definition
|
||||
|
||||
use std::arch::naked_asm;
|
||||
|
||||
extern "C" {
|
||||
#[naked] //~ ERROR should be applied to a function definition
|
||||
#[unsafe(naked)] //~ ERROR should be applied to a function definition
|
||||
fn f();
|
||||
}
|
||||
|
||||
#[naked] //~ ERROR should be applied to a function definition
|
||||
#[unsafe(naked)] //~ ERROR should be applied to a function definition
|
||||
#[repr(C)]
|
||||
struct S {
|
||||
a: u32,
|
||||
|
@ -19,35 +19,35 @@ struct S {
|
|||
}
|
||||
|
||||
trait Invoke {
|
||||
#[naked] //~ ERROR should be applied to a function definition
|
||||
#[unsafe(naked)] //~ ERROR should be applied to a function definition
|
||||
extern "C" fn invoke(&self);
|
||||
}
|
||||
|
||||
impl Invoke for S {
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn invoke(&self) {
|
||||
unsafe { naked_asm!("") }
|
||||
naked_asm!("")
|
||||
}
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn ok() {
|
||||
unsafe { naked_asm!("") }
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
impl S {
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn g() {
|
||||
unsafe { naked_asm!("") }
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn h(&self) {
|
||||
unsafe { naked_asm!("") }
|
||||
naked_asm!("")
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[naked] //~ ERROR should be applied to a function definition
|
||||
#[unsafe(naked)] //~ ERROR should be applied to a function definition
|
||||
|| {};
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: attribute should be applied to a function definition
|
||||
--> $DIR/naked-invalid-attr.rs:14:1
|
||||
|
|
||||
LL | #[naked]
|
||||
| ^^^^^^^^
|
||||
LL | #[unsafe(naked)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
LL | #[repr(C)]
|
||||
LL | / struct S {
|
||||
LL | | a: u32,
|
||||
|
@ -13,32 +13,32 @@ LL | | }
|
|||
error: attribute should be applied to a function definition
|
||||
--> $DIR/naked-invalid-attr.rs:51:5
|
||||
|
|
||||
LL | #[naked]
|
||||
| ^^^^^^^^
|
||||
LL | #[unsafe(naked)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
LL | || {};
|
||||
| ----- not a function definition
|
||||
|
||||
error: attribute should be applied to a function definition
|
||||
--> $DIR/naked-invalid-attr.rs:22:5
|
||||
|
|
||||
LL | #[naked]
|
||||
| ^^^^^^^^
|
||||
LL | #[unsafe(naked)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
LL | extern "C" fn invoke(&self);
|
||||
| ---------------------------- not a function definition
|
||||
|
||||
error: attribute should be applied to a function definition
|
||||
--> $DIR/naked-invalid-attr.rs:10:5
|
||||
|
|
||||
LL | #[naked]
|
||||
| ^^^^^^^^
|
||||
LL | #[unsafe(naked)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
LL | fn f();
|
||||
| ------- not a function definition
|
||||
|
||||
error: attribute should be applied to a function definition
|
||||
--> $DIR/naked-invalid-attr.rs:5:1
|
||||
|
|
||||
LL | #![naked]
|
||||
| ^^^^^^^^^ cannot be applied to crates
|
||||
LL | #![unsafe(naked)]
|
||||
| ^^^^^^^^^^^^^^^^^ cannot be applied to crates
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
|
|
|
@ -6,43 +6,43 @@ use std::arch::naked_asm;
|
|||
|
||||
#[repr(C)]
|
||||
//~^ ERROR attribute should be applied to a struct, enum, or union [E0517]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn example1() {
|
||||
//~^ NOTE not a struct, enum, or union
|
||||
unsafe { naked_asm!("") }
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
//~^ ERROR attribute should be applied to a struct, enum, or union [E0517]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn example2() {
|
||||
//~^ NOTE not a struct, enum, or union
|
||||
unsafe { naked_asm!("") }
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
#[repr(align(16), C)]
|
||||
//~^ ERROR attribute should be applied to a struct, enum, or union [E0517]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn example3() {
|
||||
//~^ NOTE not a struct, enum, or union
|
||||
unsafe { naked_asm!("") }
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
// note: two errors because of packed and C
|
||||
#[repr(C, packed)]
|
||||
//~^ ERROR attribute should be applied to a struct or union [E0517]
|
||||
//~| ERROR attribute should be applied to a struct, enum, or union [E0517]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn example4() {
|
||||
//~^ NOTE not a struct, enum, or union
|
||||
//~| NOTE not a struct or union
|
||||
unsafe { naked_asm!("") }
|
||||
naked_asm!("")
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
//~^ ERROR attribute should be applied to an enum [E0517]
|
||||
#[naked]
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn example5() {
|
||||
//~^ NOTE not an enum
|
||||
unsafe { naked_asm!("") }
|
||||
naked_asm!("")
|
||||
}
|
||||
|
|
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