mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-04 09:53:04 +03:00
Auto merge of #104428 - matthiaskrgr:rollup-jo3078i, r=matthiaskrgr
Rollup of 13 pull requests Successful merges: - #103842 (Adding Fuchsia compiler testing script, docs) - #104354 (Remove leading newlines from `NonZero*` doc examples) - #104372 (Update compiler-builtins) - #104380 (rustdoc: remove unused CSS `code { opacity: 1 }`) - #104381 (Remove dead NoneError diagnostic handling) - #104383 (Remove unused symbols and diagnostic items) - #104391 (Deriving cleanups) - #104403 (Specify language of code comment to generate document) - #104404 (Fix missing minification for static files) - #104413 ([llvm-wrapper] adapt for LLVM API change) - #104415 (rustdoc: fix corner case in search keyboard commands) - #104422 (Fix suggest associated call syntax) - #104426 (Add test for #102154) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
@@ -392,15 +392,7 @@ pub struct Generics {
|
||||
impl Default for Generics {
|
||||
/// Creates an instance of `Generics`.
|
||||
fn default() -> Generics {
|
||||
Generics {
|
||||
params: Vec::new(),
|
||||
where_clause: WhereClause {
|
||||
has_where_token: false,
|
||||
predicates: Vec::new(),
|
||||
span: DUMMY_SP,
|
||||
},
|
||||
span: DUMMY_SP,
|
||||
}
|
||||
Generics { params: Vec::new(), where_clause: Default::default(), span: DUMMY_SP }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,6 +407,12 @@ pub struct WhereClause {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl Default for WhereClause {
|
||||
fn default() -> WhereClause {
|
||||
WhereClause { has_where_token: false, predicates: Vec::new(), span: DUMMY_SP }
|
||||
}
|
||||
}
|
||||
|
||||
/// A single predicate in a where-clause.
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub enum WherePredicate {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::deriving::generic::ty::*;
|
||||
use crate::deriving::generic::*;
|
||||
use crate::deriving::path_std;
|
||||
|
||||
@@ -19,7 +18,6 @@ pub fn expand_deriving_copy(
|
||||
path: path_std!(marker::Copy),
|
||||
skip_path_as_bound: false,
|
||||
additional_bounds: Vec::new(),
|
||||
generics: Bounds::empty(),
|
||||
supports_unions: true,
|
||||
methods: Vec::new(),
|
||||
associated_types: Vec::new(),
|
||||
|
||||
@@ -75,7 +75,6 @@ pub fn expand_deriving_clone(
|
||||
path: path_std!(clone::Clone),
|
||||
skip_path_as_bound: false,
|
||||
additional_bounds: bounds,
|
||||
generics: Bounds::empty(),
|
||||
supports_unions: true,
|
||||
methods: vec![MethodDef {
|
||||
name: sym::clone,
|
||||
|
||||
@@ -28,7 +28,6 @@ pub fn expand_deriving_eq(
|
||||
path: path_std!(cmp::Eq),
|
||||
skip_path_as_bound: false,
|
||||
additional_bounds: Vec::new(),
|
||||
generics: Bounds::empty(),
|
||||
supports_unions: true,
|
||||
methods: vec![MethodDef {
|
||||
name: sym::assert_receiver_is_total_eq,
|
||||
|
||||
@@ -22,7 +22,6 @@ pub fn expand_deriving_ord(
|
||||
path: path_std!(cmp::Ord),
|
||||
skip_path_as_bound: false,
|
||||
additional_bounds: Vec::new(),
|
||||
generics: Bounds::empty(),
|
||||
supports_unions: false,
|
||||
methods: vec![MethodDef {
|
||||
name: sym::cmp,
|
||||
|
||||
@@ -86,7 +86,6 @@ fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOr
|
||||
path: path_std!(cmp::PartialEq),
|
||||
skip_path_as_bound: false,
|
||||
additional_bounds: Vec::new(),
|
||||
generics: Bounds::empty(),
|
||||
supports_unions: false,
|
||||
methods,
|
||||
associated_types: Vec::new(),
|
||||
|
||||
@@ -40,7 +40,6 @@ pub fn expand_deriving_partial_ord(
|
||||
path: path_std!(cmp::PartialOrd),
|
||||
skip_path_as_bound: false,
|
||||
additional_bounds: vec![],
|
||||
generics: Bounds::empty(),
|
||||
supports_unions: false,
|
||||
methods: vec![partial_cmp_def],
|
||||
associated_types: Vec::new(),
|
||||
|
||||
@@ -23,7 +23,6 @@ pub fn expand_deriving_debug(
|
||||
path: path_std!(fmt::Debug),
|
||||
skip_path_as_bound: false,
|
||||
additional_bounds: Vec::new(),
|
||||
generics: Bounds::empty(),
|
||||
supports_unions: false,
|
||||
methods: vec![MethodDef {
|
||||
name: sym::fmt,
|
||||
|
||||
@@ -26,7 +26,6 @@ pub fn expand_deriving_rustc_decodable(
|
||||
path: Path::new_(vec![krate, sym::Decodable], vec![], PathKind::Global),
|
||||
skip_path_as_bound: false,
|
||||
additional_bounds: Vec::new(),
|
||||
generics: Bounds::empty(),
|
||||
supports_unions: false,
|
||||
methods: vec![MethodDef {
|
||||
name: sym::decode,
|
||||
|
||||
@@ -27,7 +27,6 @@ pub fn expand_deriving_default(
|
||||
path: Path::new(vec![kw::Default, sym::Default]),
|
||||
skip_path_as_bound: has_a_default_variant(item),
|
||||
additional_bounds: Vec::new(),
|
||||
generics: Bounds::empty(),
|
||||
supports_unions: false,
|
||||
methods: vec![MethodDef {
|
||||
name: kw::Default,
|
||||
|
||||
@@ -110,7 +110,6 @@ pub fn expand_deriving_rustc_encodable(
|
||||
path: Path::new_(vec![krate, sym::Encodable], vec![], PathKind::Global),
|
||||
skip_path_as_bound: false,
|
||||
additional_bounds: Vec::new(),
|
||||
generics: Bounds::empty(),
|
||||
supports_unions: false,
|
||||
methods: vec![MethodDef {
|
||||
name: sym::encode,
|
||||
|
||||
@@ -195,9 +195,6 @@ pub struct TraitDef<'a> {
|
||||
/// other than the current trait
|
||||
pub additional_bounds: Vec<Ty>,
|
||||
|
||||
/// Any extra lifetimes and/or bounds, e.g., `D: serialize::Decoder`
|
||||
pub generics: Bounds,
|
||||
|
||||
/// Can this trait be derived for unions?
|
||||
pub supports_unions: bool,
|
||||
|
||||
@@ -583,19 +580,21 @@ fn create_derived_impl(
|
||||
})
|
||||
});
|
||||
|
||||
let Generics { mut params, mut where_clause, .. } =
|
||||
self.generics.to_generics(cx, self.span, type_ident, generics);
|
||||
let mut where_clause = ast::WhereClause::default();
|
||||
where_clause.span = generics.where_clause.span;
|
||||
let ctxt = self.span.ctxt();
|
||||
let span = generics.span.with_ctxt(ctxt);
|
||||
|
||||
// Create the generic parameters
|
||||
params.extend(generics.params.iter().map(|param| match ¶m.kind {
|
||||
GenericParamKind::Lifetime { .. } => param.clone(),
|
||||
GenericParamKind::Type { .. } => {
|
||||
// I don't think this can be moved out of the loop, since
|
||||
// a GenericBound requires an ast id
|
||||
let bounds: Vec<_> =
|
||||
let params: Vec<_> = generics
|
||||
.params
|
||||
.iter()
|
||||
.map(|param| match ¶m.kind {
|
||||
GenericParamKind::Lifetime { .. } => param.clone(),
|
||||
GenericParamKind::Type { .. } => {
|
||||
// I don't think this can be moved out of the loop, since
|
||||
// a GenericBound requires an ast id
|
||||
let bounds: Vec<_> =
|
||||
// extra restrictions on the generics parameters to the
|
||||
// type being derived upon
|
||||
self.additional_bounds.iter().map(|p| {
|
||||
@@ -608,21 +607,22 @@ fn create_derived_impl(
|
||||
param.bounds.iter().cloned()
|
||||
).collect();
|
||||
|
||||
cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, bounds, None)
|
||||
}
|
||||
GenericParamKind::Const { ty, kw_span, .. } => {
|
||||
let const_nodefault_kind = GenericParamKind::Const {
|
||||
ty: ty.clone(),
|
||||
kw_span: kw_span.with_ctxt(ctxt),
|
||||
cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, bounds, None)
|
||||
}
|
||||
GenericParamKind::Const { ty, kw_span, .. } => {
|
||||
let const_nodefault_kind = GenericParamKind::Const {
|
||||
ty: ty.clone(),
|
||||
kw_span: kw_span.with_ctxt(ctxt),
|
||||
|
||||
// We can't have default values inside impl block
|
||||
default: None,
|
||||
};
|
||||
let mut param_clone = param.clone();
|
||||
param_clone.kind = const_nodefault_kind;
|
||||
param_clone
|
||||
}
|
||||
}));
|
||||
// We can't have default values inside impl block
|
||||
default: None,
|
||||
};
|
||||
let mut param_clone = param.clone();
|
||||
param_clone.kind = const_nodefault_kind;
|
||||
param_clone
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// and similarly for where clauses
|
||||
where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| {
|
||||
@@ -1062,18 +1062,15 @@ fn expand_struct_method_body<'b>(
|
||||
trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, true);
|
||||
mk_body(cx, selflike_fields)
|
||||
} else {
|
||||
// Neither packed nor copy. Need to use ref patterns.
|
||||
// Packed and not copy. Need to use ref patterns.
|
||||
let prefixes: Vec<_> =
|
||||
(0..selflike_args.len()).map(|i| format!("__self_{}", i)).collect();
|
||||
let addr_of = always_copy;
|
||||
let selflike_fields =
|
||||
trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, addr_of);
|
||||
let selflike_fields = trait_.create_struct_pattern_fields(cx, struct_def, &prefixes);
|
||||
let mut body = mk_body(cx, selflike_fields);
|
||||
|
||||
let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]);
|
||||
let by_ref = ByRef::from(is_packed && !always_copy);
|
||||
let patterns =
|
||||
trait_.create_struct_patterns(cx, struct_path, struct_def, &prefixes, by_ref);
|
||||
trait_.create_struct_patterns(cx, struct_path, struct_def, &prefixes, ByRef::Yes);
|
||||
|
||||
// Do the let-destructuring.
|
||||
let mut stmts: Vec<_> = iter::zip(selflike_args, patterns)
|
||||
@@ -1254,9 +1251,7 @@ fn expand_enum_method_body<'b>(
|
||||
// A single arm has form (&VariantK, &VariantK, ...) => BodyK
|
||||
// (see "Final wrinkle" note below for why.)
|
||||
|
||||
let addr_of = false; // because enums can't be repr(packed)
|
||||
let fields =
|
||||
trait_.create_struct_pattern_fields(cx, &variant.data, &prefixes, addr_of);
|
||||
let fields = trait_.create_struct_pattern_fields(cx, &variant.data, &prefixes);
|
||||
|
||||
let sp = variant.span.with_ctxt(trait_.span.ctxt());
|
||||
let variant_path = cx.path(sp, vec![type_ident, variant.ident]);
|
||||
@@ -1519,15 +1514,13 @@ fn create_struct_pattern_fields(
|
||||
cx: &mut ExtCtxt<'_>,
|
||||
struct_def: &'a VariantData,
|
||||
prefixes: &[String],
|
||||
addr_of: bool,
|
||||
) -> Vec<FieldInfo> {
|
||||
self.create_fields(struct_def, |i, _struct_field, sp| {
|
||||
prefixes
|
||||
.iter()
|
||||
.map(|prefix| {
|
||||
let ident = self.mk_pattern_ident(prefix, i);
|
||||
let expr = cx.expr_path(cx.path_ident(sp, ident));
|
||||
if addr_of { cx.expr_addr_of(sp, expr) } else { expr }
|
||||
cx.expr_path(cx.path_ident(sp, ident))
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
|
||||
@@ -25,7 +25,6 @@ pub fn expand_deriving_hash(
|
||||
path,
|
||||
skip_path_as_bound: false,
|
||||
additional_bounds: Vec::new(),
|
||||
generics: Bounds::empty(),
|
||||
supports_unions: false,
|
||||
methods: vec![MethodDef {
|
||||
name: sym::hash,
|
||||
|
||||
@@ -20,11 +20,10 @@
|
||||
};
|
||||
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||
use rustc_middle::traits::util::supertraits;
|
||||
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
|
||||
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
|
||||
use rustc_middle::ty::print::with_crate_prefix;
|
||||
use rustc_middle::ty::{
|
||||
self, DefIdTree, GenericArg, GenericArgKind, ToPredicate, Ty, TyCtxt, TypeVisitable,
|
||||
};
|
||||
use rustc_middle::ty::{self, DefIdTree, GenericArgKind, ToPredicate, Ty, TyCtxt, TypeVisitable};
|
||||
use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef};
|
||||
use rustc_span::symbol::{kw, sym, Ident};
|
||||
use rustc_span::Symbol;
|
||||
@@ -263,15 +262,15 @@ pub fn report_method_error(
|
||||
}) => {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let actual = self.resolve_vars_if_possible(rcvr_ty);
|
||||
let ty_str = self.ty_to_string(actual);
|
||||
let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
|
||||
let ty_str = self.ty_to_string(rcvr_ty);
|
||||
let is_method = mode == Mode::MethodCall;
|
||||
let item_kind = if is_method {
|
||||
"method"
|
||||
} else if actual.is_enum() {
|
||||
} else if rcvr_ty.is_enum() {
|
||||
"variant or associated item"
|
||||
} else {
|
||||
match (item_name.as_str().chars().next(), actual.is_fresh_ty()) {
|
||||
match (item_name.as_str().chars().next(), rcvr_ty.is_fresh_ty()) {
|
||||
(Some(name), false) if name.is_lowercase() => "function or associated item",
|
||||
(Some(_), false) => "associated item",
|
||||
(Some(_), true) | (None, false) => "variant or associated item",
|
||||
@@ -280,9 +279,9 @@ pub fn report_method_error(
|
||||
};
|
||||
|
||||
if self.suggest_wrapping_range_with_parens(
|
||||
tcx, actual, source, span, item_name, &ty_str,
|
||||
tcx, rcvr_ty, source, span, item_name, &ty_str,
|
||||
) || self.suggest_constraining_numerical_ty(
|
||||
tcx, actual, source, span, item_kind, item_name, &ty_str,
|
||||
tcx, rcvr_ty, source, span, item_kind, item_name, &ty_str,
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
@@ -290,9 +289,9 @@ pub fn report_method_error(
|
||||
|
||||
// Don't show generic arguments when the method can't be found in any implementation (#81576).
|
||||
let mut ty_str_reported = ty_str.clone();
|
||||
if let ty::Adt(_, generics) = actual.kind() {
|
||||
if let ty::Adt(_, generics) = rcvr_ty.kind() {
|
||||
if generics.len() > 0 {
|
||||
let mut autoderef = self.autoderef(span, actual);
|
||||
let mut autoderef = self.autoderef(span, rcvr_ty);
|
||||
let candidate_found = autoderef.any(|(ty, _)| {
|
||||
if let ty::Adt(adt_def, _) = ty.kind() {
|
||||
self.tcx
|
||||
@@ -321,16 +320,16 @@ pub fn report_method_error(
|
||||
"no {} named `{}` found for {} `{}` in the current scope",
|
||||
item_kind,
|
||||
item_name,
|
||||
actual.prefix_string(self.tcx),
|
||||
rcvr_ty.prefix_string(self.tcx),
|
||||
ty_str_reported,
|
||||
);
|
||||
if actual.references_error() {
|
||||
if rcvr_ty.references_error() {
|
||||
err.downgrade_to_delayed_bug();
|
||||
}
|
||||
|
||||
if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
|
||||
self.suggest_await_before_method(
|
||||
&mut err, item_name, actual, cal, span,
|
||||
&mut err, item_name, rcvr_ty, cal, span,
|
||||
);
|
||||
}
|
||||
if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) {
|
||||
@@ -341,7 +340,7 @@ pub fn report_method_error(
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
if let ty::RawPtr(_) = &actual.kind() {
|
||||
if let ty::RawPtr(_) = &rcvr_ty.kind() {
|
||||
err.note(
|
||||
"try using `<*const T>::as_ref()` to get a reference to the \
|
||||
type behind the pointer: https://doc.rust-lang.org/std/\
|
||||
@@ -353,7 +352,7 @@ pub fn report_method_error(
|
||||
);
|
||||
}
|
||||
|
||||
let ty_span = match actual.kind() {
|
||||
let ty_span = match rcvr_ty.kind() {
|
||||
ty::Param(param_type) => Some(
|
||||
param_type.span_from_generics(self.tcx, self.body_id.owner.to_def_id()),
|
||||
),
|
||||
@@ -365,7 +364,7 @@ pub fn report_method_error(
|
||||
span,
|
||||
format!(
|
||||
"{item_kind} `{item_name}` not found for this {}",
|
||||
actual.prefix_string(self.tcx)
|
||||
rcvr_ty.prefix_string(self.tcx)
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -398,122 +397,15 @@ pub fn report_method_error(
|
||||
custom_span_label = true;
|
||||
}
|
||||
if static_candidates.len() == 1 {
|
||||
let mut has_unsuggestable_args = false;
|
||||
let ty_str = if let Some(CandidateSource::Impl(impl_did)) =
|
||||
static_candidates.get(0)
|
||||
{
|
||||
// When the "method" is resolved through dereferencing, we really want the
|
||||
// original type that has the associated function for accurate suggestions.
|
||||
// (#61411)
|
||||
let ty = tcx.at(span).type_of(*impl_did);
|
||||
match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) {
|
||||
(ty::Adt(def, _), ty::Adt(def_actual, substs)) if def == def_actual => {
|
||||
// If there are any inferred arguments, (`{integer}`), we should replace
|
||||
// them with underscores to allow the compiler to infer them
|
||||
let infer_substs: Vec<GenericArg<'_>> = substs
|
||||
.into_iter()
|
||||
.map(|arg| {
|
||||
if !arg.is_suggestable(tcx, true) {
|
||||
has_unsuggestable_args = true;
|
||||
match arg.unpack() {
|
||||
GenericArgKind::Lifetime(_) => self
|
||||
.next_region_var(RegionVariableOrigin::MiscVariable(
|
||||
rustc_span::DUMMY_SP,
|
||||
))
|
||||
.into(),
|
||||
GenericArgKind::Type(_) => self
|
||||
.next_ty_var(TypeVariableOrigin {
|
||||
span: rustc_span::DUMMY_SP,
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
})
|
||||
.into(),
|
||||
GenericArgKind::Const(arg) => self
|
||||
.next_const_var(
|
||||
arg.ty(),
|
||||
ConstVariableOrigin {
|
||||
span: rustc_span::DUMMY_SP,
|
||||
kind: ConstVariableOriginKind::MiscVariable,
|
||||
},
|
||||
)
|
||||
.into(),
|
||||
}
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
tcx.value_path_str_with_substs(
|
||||
def_actual.did(),
|
||||
tcx.intern_substs(&infer_substs),
|
||||
)
|
||||
}
|
||||
_ => self.ty_to_value_string(ty.peel_refs()),
|
||||
}
|
||||
} else {
|
||||
self.ty_to_value_string(actual.peel_refs())
|
||||
};
|
||||
if let SelfSource::MethodCall(_) = source {
|
||||
let first_arg = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) &&
|
||||
let Some(assoc) = self.associated_value(*impl_did, item_name) {
|
||||
let sig = self.tcx.fn_sig(assoc.def_id);
|
||||
if let Some(first) = sig.inputs().skip_binder().get(0) {
|
||||
if first.peel_refs() == rcvr_ty.peel_refs() {
|
||||
None
|
||||
} else {
|
||||
Some(if first.is_region_ptr() {
|
||||
if first.is_mutable_ptr() { "&mut " } else { "&" }
|
||||
} else {
|
||||
""
|
||||
})
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let args = if let Some((receiver, args)) = args {
|
||||
// The first arg is the same kind as the receiver
|
||||
let explicit_args = if first_arg.is_some() {
|
||||
std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>()
|
||||
} else {
|
||||
// There is no `Self` kind to infer the arguments from
|
||||
if has_unsuggestable_args {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
}
|
||||
args.iter().collect()
|
||||
};
|
||||
format!(
|
||||
"({}{})",
|
||||
first_arg.unwrap_or(""),
|
||||
explicit_args
|
||||
.iter()
|
||||
.map(|arg| tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.span_to_snippet(arg.span)
|
||||
.unwrap_or_else(|_| {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
"_".to_owned()
|
||||
}))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
)
|
||||
} else {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
"(...)".to_owned()
|
||||
};
|
||||
err.span_suggestion(
|
||||
sugg_span,
|
||||
"use associated function syntax instead",
|
||||
format!("{}::{}{}", ty_str, item_name, args),
|
||||
applicability,
|
||||
);
|
||||
} else {
|
||||
err.help(&format!("try with `{}::{}`", ty_str, item_name,));
|
||||
}
|
||||
self.suggest_associated_call_syntax(
|
||||
&mut err,
|
||||
&static_candidates,
|
||||
rcvr_ty,
|
||||
source,
|
||||
item_name,
|
||||
args,
|
||||
sugg_span,
|
||||
);
|
||||
|
||||
report_candidates(span, &mut err, &mut static_candidates, sugg_span);
|
||||
} else if static_candidates.len() > 1 {
|
||||
@@ -523,7 +415,7 @@ pub fn report_method_error(
|
||||
let mut bound_spans = vec![];
|
||||
let mut restrict_type_params = false;
|
||||
let mut unsatisfied_bounds = false;
|
||||
if item_name.name == sym::count && self.is_slice_ty(actual, span) {
|
||||
if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) {
|
||||
let msg = "consider using `len` instead";
|
||||
if let SelfSource::MethodCall(_expr) = source {
|
||||
err.span_suggestion_short(
|
||||
@@ -537,7 +429,7 @@ pub fn report_method_error(
|
||||
}
|
||||
if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) {
|
||||
let iterator_trait = self.tcx.def_path_str(iterator_trait);
|
||||
err.note(&format!("`count` is defined on `{iterator_trait}`, which `{actual}` does not implement"));
|
||||
err.note(&format!("`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"));
|
||||
}
|
||||
} else if !unsatisfied_predicates.is_empty() {
|
||||
let mut type_params = FxHashMap::default();
|
||||
@@ -876,7 +768,7 @@ trait bound{s}",
|
||||
.map(|(_, path)| path)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let actual_prefix = actual.prefix_string(self.tcx);
|
||||
let actual_prefix = rcvr_ty.prefix_string(self.tcx);
|
||||
info!("unimplemented_traits.len() == {}", unimplemented_traits.len());
|
||||
let (primary_message, label) =
|
||||
if unimplemented_traits.len() == 1 && unimplemented_traits_only {
|
||||
@@ -885,7 +777,7 @@ trait bound{s}",
|
||||
.next()
|
||||
.map(|(_, (trait_ref, obligation))| {
|
||||
if trait_ref.self_ty().references_error()
|
||||
|| actual.references_error()
|
||||
|| rcvr_ty.references_error()
|
||||
{
|
||||
// Avoid crashing.
|
||||
return (None, None);
|
||||
@@ -921,7 +813,7 @@ trait bound{s}",
|
||||
let label_span_not_found = |err: &mut Diagnostic| {
|
||||
if unsatisfied_predicates.is_empty() {
|
||||
err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
|
||||
let is_string_or_ref_str = match actual.kind() {
|
||||
let is_string_or_ref_str = match rcvr_ty.kind() {
|
||||
ty::Ref(_, ty, _) => {
|
||||
ty.is_str()
|
||||
|| matches!(
|
||||
@@ -957,7 +849,7 @@ trait bound{s}",
|
||||
// different from the received one
|
||||
// So we avoid suggestion method with Box<Self>
|
||||
// for instance
|
||||
self.tcx.at(span).type_of(*def_id) != actual
|
||||
self.tcx.at(span).type_of(*def_id) != rcvr_ty
|
||||
&& self.tcx.at(span).type_of(*def_id) != rcvr_ty
|
||||
}
|
||||
(Mode::Path, false, _) => true,
|
||||
@@ -1017,10 +909,12 @@ trait bound{s}",
|
||||
// Don't suggest (for example) `expr.field.clone()` if `expr.clone()`
|
||||
// can't be called due to `typeof(expr): Clone` not holding.
|
||||
if unsatisfied_predicates.is_empty() {
|
||||
self.suggest_calling_method_on_field(&mut err, source, span, actual, item_name);
|
||||
self.suggest_calling_method_on_field(
|
||||
&mut err, source, span, rcvr_ty, item_name,
|
||||
);
|
||||
}
|
||||
|
||||
self.check_for_inner_self(&mut err, source, span, actual, item_name);
|
||||
self.check_for_inner_self(&mut err, source, span, rcvr_ty, item_name);
|
||||
|
||||
bound_spans.sort();
|
||||
bound_spans.dedup();
|
||||
@@ -1028,7 +922,7 @@ trait bound{s}",
|
||||
err.span_label(span, &msg);
|
||||
}
|
||||
|
||||
if actual.is_numeric() && actual.is_fresh() || restrict_type_params {
|
||||
if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params {
|
||||
} else {
|
||||
self.suggest_traits_to_import(
|
||||
&mut err,
|
||||
@@ -1046,8 +940,8 @@ trait bound{s}",
|
||||
|
||||
// Don't emit a suggestion if we found an actual method
|
||||
// that had unsatisfied trait bounds
|
||||
if unsatisfied_predicates.is_empty() && actual.is_enum() {
|
||||
let adt_def = actual.ty_adt_def().expect("enum is not an ADT");
|
||||
if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() {
|
||||
let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT");
|
||||
if let Some(suggestion) = lev_distance::find_best_match_for_name(
|
||||
&adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(),
|
||||
item_name.name,
|
||||
@@ -1062,7 +956,7 @@ trait bound{s}",
|
||||
}
|
||||
}
|
||||
|
||||
if item_name.name == sym::as_str && actual.peel_refs().is_str() {
|
||||
if item_name.name == sym::as_str && rcvr_ty.peel_refs().is_str() {
|
||||
let msg = "remove this method call";
|
||||
let mut fallback_span = true;
|
||||
if let SelfSource::MethodCall(expr) = source {
|
||||
@@ -1178,6 +1072,138 @@ trait bound{s}",
|
||||
None
|
||||
}
|
||||
|
||||
/// Suggest calling `Ty::method` if `.method()` isn't found because the method
|
||||
/// doesn't take a `self` receiver.
|
||||
fn suggest_associated_call_syntax(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
static_candidates: &Vec<CandidateSource>,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
source: SelfSource<'tcx>,
|
||||
item_name: Ident,
|
||||
args: Option<(&hir::Expr<'tcx>, &[hir::Expr<'tcx>])>,
|
||||
sugg_span: Span,
|
||||
) {
|
||||
let mut has_unsuggestable_args = false;
|
||||
let ty_str = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) {
|
||||
// When the "method" is resolved through dereferencing, we really want the
|
||||
// original type that has the associated function for accurate suggestions.
|
||||
// (#61411)
|
||||
let impl_ty = self.tcx.type_of(*impl_did);
|
||||
let target_ty = self
|
||||
.autoderef(sugg_span, rcvr_ty)
|
||||
.find(|(rcvr_ty, _)| {
|
||||
DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer }
|
||||
.types_may_unify(*rcvr_ty, impl_ty)
|
||||
})
|
||||
.map_or(impl_ty, |(ty, _)| ty)
|
||||
.peel_refs();
|
||||
if let ty::Adt(def, substs) = target_ty.kind() {
|
||||
// If there are any inferred arguments, (`{integer}`), we should replace
|
||||
// them with underscores to allow the compiler to infer them
|
||||
let infer_substs = self.tcx.mk_substs(substs.into_iter().map(|arg| {
|
||||
if !arg.is_suggestable(self.tcx, true) {
|
||||
has_unsuggestable_args = true;
|
||||
match arg.unpack() {
|
||||
GenericArgKind::Lifetime(_) => self
|
||||
.next_region_var(RegionVariableOrigin::MiscVariable(
|
||||
rustc_span::DUMMY_SP,
|
||||
))
|
||||
.into(),
|
||||
GenericArgKind::Type(_) => self
|
||||
.next_ty_var(TypeVariableOrigin {
|
||||
span: rustc_span::DUMMY_SP,
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
})
|
||||
.into(),
|
||||
GenericArgKind::Const(arg) => self
|
||||
.next_const_var(
|
||||
arg.ty(),
|
||||
ConstVariableOrigin {
|
||||
span: rustc_span::DUMMY_SP,
|
||||
kind: ConstVariableOriginKind::MiscVariable,
|
||||
},
|
||||
)
|
||||
.into(),
|
||||
}
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
}));
|
||||
|
||||
self.tcx.value_path_str_with_substs(def.did(), infer_substs)
|
||||
} else {
|
||||
self.ty_to_value_string(target_ty)
|
||||
}
|
||||
} else {
|
||||
self.ty_to_value_string(rcvr_ty.peel_refs())
|
||||
};
|
||||
if let SelfSource::MethodCall(_) = source {
|
||||
let first_arg = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0)
|
||||
&& let Some(assoc) = self.associated_value(*impl_did, item_name)
|
||||
&& assoc.kind == ty::AssocKind::Fn
|
||||
{
|
||||
let sig = self.tcx.fn_sig(assoc.def_id);
|
||||
if let Some(first) = sig.inputs().skip_binder().get(0) {
|
||||
if first.peel_refs() == rcvr_ty.peel_refs() {
|
||||
None
|
||||
} else {
|
||||
Some(if first.is_region_ptr() {
|
||||
if first.is_mutable_ptr() { "&mut " } else { "&" }
|
||||
} else {
|
||||
""
|
||||
})
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let args = if let Some((receiver, args)) = args {
|
||||
// The first arg is the same kind as the receiver
|
||||
let explicit_args = if first_arg.is_some() {
|
||||
std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>()
|
||||
} else {
|
||||
// There is no `Self` kind to infer the arguments from
|
||||
if has_unsuggestable_args {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
}
|
||||
args.iter().collect()
|
||||
};
|
||||
format!(
|
||||
"({}{})",
|
||||
first_arg.unwrap_or(""),
|
||||
explicit_args
|
||||
.iter()
|
||||
.map(|arg| self
|
||||
.tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.span_to_snippet(arg.span)
|
||||
.unwrap_or_else(|_| {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
"_".to_owned()
|
||||
}))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
)
|
||||
} else {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
"(...)".to_owned()
|
||||
};
|
||||
err.span_suggestion(
|
||||
sugg_span,
|
||||
"use associated function syntax instead",
|
||||
format!("{}::{}{}", ty_str, item_name, args),
|
||||
applicability,
|
||||
);
|
||||
} else {
|
||||
err.help(&format!("try with `{}::{}`", ty_str, item_name,));
|
||||
}
|
||||
}
|
||||
|
||||
/// Suggest calling a field with a type that implements the `Fn*` traits instead of a method with
|
||||
/// the same name as the field i.e. `(a.my_fn_ptr)(10)` instead of `a.my_fn_ptr(10)`.
|
||||
fn suggest_calling_field_as_fn(
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
/// scope.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// struct SomeStruct;
|
||||
/// impl Drop for SomeStruct {
|
||||
/// fn drop(&mut self) {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "llvm/IR/IntrinsicsARM.h"
|
||||
#include "llvm/IR/Mangler.h"
|
||||
#if LLVM_VERSION_GE(16, 0)
|
||||
#include "llvm/IR/ModRef.h"
|
||||
#include "llvm/Support/ModRef.h"
|
||||
#endif
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/COFFImportFile.h"
|
||||
|
||||
@@ -170,8 +170,6 @@
|
||||
Count,
|
||||
Cow,
|
||||
Debug,
|
||||
DebugStruct,
|
||||
DebugTuple,
|
||||
Decodable,
|
||||
Decoder,
|
||||
DecorateLint,
|
||||
@@ -190,9 +188,6 @@
|
||||
Error,
|
||||
File,
|
||||
FileType,
|
||||
Fn,
|
||||
FnMut,
|
||||
FnOnce,
|
||||
FormatSpec,
|
||||
Formatter,
|
||||
From,
|
||||
@@ -211,7 +206,6 @@
|
||||
Input,
|
||||
Into,
|
||||
IntoDiagnostic,
|
||||
IntoFuture,
|
||||
IntoIterator,
|
||||
IoRead,
|
||||
IoWrite,
|
||||
@@ -256,7 +250,6 @@
|
||||
Pointer,
|
||||
Poll,
|
||||
ProcMacro,
|
||||
ProcMacroHack,
|
||||
ProceduralMasqueradeDummyType,
|
||||
Range,
|
||||
RangeFrom,
|
||||
@@ -332,7 +325,6 @@
|
||||
abi_vectorcall,
|
||||
abi_x86_interrupt,
|
||||
abort,
|
||||
aborts,
|
||||
add,
|
||||
add_assign,
|
||||
add_with_overflow,
|
||||
@@ -344,7 +336,6 @@
|
||||
align,
|
||||
align_offset,
|
||||
alignment,
|
||||
alignstack,
|
||||
all,
|
||||
alloc,
|
||||
alloc_error_handler,
|
||||
@@ -433,7 +424,6 @@
|
||||
bool,
|
||||
borrowck_graphviz_format,
|
||||
borrowck_graphviz_postflow,
|
||||
borrowck_graphviz_preflow,
|
||||
box_free,
|
||||
box_patterns,
|
||||
box_syntax,
|
||||
@@ -462,7 +452,6 @@
|
||||
cfg_doctest,
|
||||
cfg_eval,
|
||||
cfg_hide,
|
||||
cfg_macro,
|
||||
cfg_panic,
|
||||
cfg_sanitize,
|
||||
cfg_target_abi,
|
||||
@@ -470,7 +459,6 @@
|
||||
cfg_target_feature,
|
||||
cfg_target_has_atomic,
|
||||
cfg_target_has_atomic_equal_alignment,
|
||||
cfg_target_has_atomic_load_store,
|
||||
cfg_target_thread_local,
|
||||
cfg_target_vendor,
|
||||
cfg_version,
|
||||
@@ -495,19 +483,15 @@
|
||||
cold,
|
||||
collapse_debuginfo,
|
||||
column,
|
||||
column_macro,
|
||||
compare_and_swap,
|
||||
compare_exchange,
|
||||
compare_exchange_weak,
|
||||
compile_error,
|
||||
compile_error_macro,
|
||||
compiler,
|
||||
compiler_builtins,
|
||||
compiler_fence,
|
||||
concat,
|
||||
concat_bytes,
|
||||
concat_idents,
|
||||
concat_macro,
|
||||
conservative_impl_trait,
|
||||
console,
|
||||
const_allocate,
|
||||
@@ -528,7 +512,6 @@
|
||||
const_fn_unsize,
|
||||
const_for,
|
||||
const_format_args,
|
||||
const_generic_defaults,
|
||||
const_generics,
|
||||
const_generics_defaults,
|
||||
const_if_match,
|
||||
@@ -547,22 +530,19 @@
|
||||
const_trait,
|
||||
const_trait_bound_opt_out,
|
||||
const_trait_impl,
|
||||
const_transmute,
|
||||
const_try,
|
||||
constant,
|
||||
constructor,
|
||||
contents,
|
||||
context,
|
||||
convert,
|
||||
copy,
|
||||
copy_closures,
|
||||
copy_nonoverlapping,
|
||||
copysignf32,
|
||||
copysignf64,
|
||||
core,
|
||||
core_intrinsics,
|
||||
core_panic,
|
||||
core_panic_2015_macro,
|
||||
core_panic_2021_macro,
|
||||
core_panic_macro,
|
||||
cosf32,
|
||||
cosf64,
|
||||
@@ -598,7 +578,6 @@
|
||||
debug_assertions,
|
||||
debug_struct,
|
||||
debug_struct_fields_finish,
|
||||
debug_trait_builder,
|
||||
debug_tuple,
|
||||
debug_tuple_fields_finish,
|
||||
debugger_visualizer,
|
||||
@@ -630,7 +609,6 @@
|
||||
discriminant_type,
|
||||
discriminant_value,
|
||||
dispatch_from_dyn,
|
||||
display_trait,
|
||||
div,
|
||||
div_assign,
|
||||
doc,
|
||||
@@ -661,7 +639,6 @@
|
||||
dyn_star,
|
||||
dyn_trait,
|
||||
e,
|
||||
edition_macro_pats,
|
||||
edition_panic,
|
||||
eh_catch_typeinfo,
|
||||
eh_personality,
|
||||
@@ -674,7 +651,6 @@
|
||||
encode,
|
||||
end,
|
||||
env,
|
||||
env_macro,
|
||||
eprint_macro,
|
||||
eprintln_macro,
|
||||
eq,
|
||||
@@ -724,9 +700,7 @@
|
||||
field,
|
||||
field_init_shorthand,
|
||||
file,
|
||||
file_macro,
|
||||
fill,
|
||||
finish,
|
||||
flags,
|
||||
float,
|
||||
float_to_int_unchecked,
|
||||
@@ -735,8 +709,6 @@
|
||||
fmaf32,
|
||||
fmaf64,
|
||||
fmt,
|
||||
fmt_as_str,
|
||||
fmt_internals,
|
||||
fmul_fast,
|
||||
fn_align,
|
||||
fn_must_use,
|
||||
@@ -751,7 +723,6 @@
|
||||
format_args_macro,
|
||||
format_args_nl,
|
||||
format_macro,
|
||||
fp,
|
||||
freeze,
|
||||
freg,
|
||||
frem_fast,
|
||||
@@ -814,7 +785,6 @@
|
||||
ignore,
|
||||
impl_header_lifetime_elision,
|
||||
impl_lint_pass,
|
||||
impl_macros,
|
||||
impl_trait_in_bindings,
|
||||
impl_trait_in_fn_trait_return,
|
||||
implied_by,
|
||||
@@ -826,7 +796,6 @@
|
||||
include,
|
||||
include_bytes,
|
||||
include_bytes_macro,
|
||||
include_macro,
|
||||
include_str,
|
||||
include_str_macro,
|
||||
inclusive_range_syntax,
|
||||
@@ -844,7 +813,6 @@
|
||||
instruction_set,
|
||||
integer_: "integer",
|
||||
integral,
|
||||
intel,
|
||||
into_future,
|
||||
into_iter,
|
||||
intra_doc_pointers,
|
||||
@@ -881,7 +849,6 @@
|
||||
lifetimes,
|
||||
likely,
|
||||
line,
|
||||
line_macro,
|
||||
link,
|
||||
link_args,
|
||||
link_cfg,
|
||||
@@ -926,7 +893,6 @@
|
||||
masked,
|
||||
match_beginning_vert,
|
||||
match_default_bindings,
|
||||
matches_macro,
|
||||
maxnumf32,
|
||||
maxnumf64,
|
||||
may_dangle,
|
||||
@@ -965,7 +931,6 @@
|
||||
modifiers,
|
||||
module,
|
||||
module_path,
|
||||
module_path_macro,
|
||||
more_qualified_paths,
|
||||
more_struct_aliases,
|
||||
movbe_target_feature,
|
||||
@@ -1035,7 +1000,6 @@
|
||||
non_exhaustive,
|
||||
non_exhaustive_omitted_patterns_lint,
|
||||
non_modrs_mods,
|
||||
none_error,
|
||||
nontemporal_store,
|
||||
noop_method_borrow,
|
||||
noop_method_clone,
|
||||
@@ -1060,7 +1024,6 @@
|
||||
optin_builtin_traits,
|
||||
option,
|
||||
option_env,
|
||||
option_env_macro,
|
||||
options,
|
||||
or,
|
||||
or_patterns,
|
||||
@@ -1103,7 +1066,6 @@
|
||||
plugins,
|
||||
pointee_trait,
|
||||
pointer,
|
||||
pointer_trait_fmt,
|
||||
poll,
|
||||
position,
|
||||
post_dash_lto: "post-lto",
|
||||
@@ -1130,7 +1092,6 @@
|
||||
proc_dash_macro: "proc-macro",
|
||||
proc_macro,
|
||||
proc_macro_attribute,
|
||||
proc_macro_def_site,
|
||||
proc_macro_derive,
|
||||
proc_macro_expr,
|
||||
proc_macro_gen,
|
||||
@@ -1231,9 +1192,6 @@
|
||||
rust_cold_cc,
|
||||
rust_eh_catch_typeinfo,
|
||||
rust_eh_personality,
|
||||
rust_eh_register_frames,
|
||||
rust_eh_unregister_frames,
|
||||
rust_oom,
|
||||
rustc,
|
||||
rustc_allocator,
|
||||
rustc_allocator_zeroed,
|
||||
@@ -1306,7 +1264,6 @@
|
||||
rustc_serialize,
|
||||
rustc_skip_array_during_method_dispatch,
|
||||
rustc_specialization_trait,
|
||||
rustc_stable,
|
||||
rustc_std_internal_symbol,
|
||||
rustc_strict_coherence,
|
||||
rustc_symbol_name,
|
||||
@@ -1434,7 +1391,6 @@
|
||||
static_recursion,
|
||||
staticlib,
|
||||
std,
|
||||
std_inject,
|
||||
std_panic,
|
||||
std_panic_2015_macro,
|
||||
std_panic_macro,
|
||||
@@ -1449,7 +1405,6 @@
|
||||
str_trim_start,
|
||||
strict_provenance,
|
||||
stringify,
|
||||
stringify_macro,
|
||||
struct_field_attributes,
|
||||
struct_inherit,
|
||||
struct_variant,
|
||||
@@ -1477,10 +1432,8 @@
|
||||
target_has_atomic_load_store,
|
||||
target_os,
|
||||
target_pointer_width,
|
||||
target_target_vendor,
|
||||
target_thread_local,
|
||||
target_vendor,
|
||||
task,
|
||||
tbm_target_feature,
|
||||
termination,
|
||||
termination_trait,
|
||||
@@ -1492,7 +1445,6 @@
|
||||
test_removed_feature,
|
||||
test_runner,
|
||||
test_unstable_lint,
|
||||
then_with,
|
||||
thread,
|
||||
thread_local,
|
||||
thread_local_macro,
|
||||
@@ -1524,7 +1476,6 @@
|
||||
try_trait_v2,
|
||||
tt,
|
||||
tuple,
|
||||
tuple_from_req,
|
||||
tuple_indexing,
|
||||
tuple_trait,
|
||||
two_phase,
|
||||
@@ -1568,7 +1519,6 @@
|
||||
unreachable_2015,
|
||||
unreachable_2015_macro,
|
||||
unreachable_2021,
|
||||
unreachable_2021_macro,
|
||||
unreachable_code,
|
||||
unreachable_display,
|
||||
unreachable_macro,
|
||||
@@ -1587,7 +1537,6 @@
|
||||
from crates.io via `Cargo.toml` instead?",
|
||||
untagged_unions,
|
||||
unused_imports,
|
||||
unused_qualifications,
|
||||
unwind,
|
||||
unwind_attributes,
|
||||
unwind_safe_trait,
|
||||
|
||||
@@ -650,41 +650,14 @@ fn report_selection_error(
|
||||
))
|
||||
);
|
||||
|
||||
if is_try_conversion {
|
||||
let none_error = self
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::none_error)
|
||||
.map(|def_id| tcx.type_of(def_id));
|
||||
let should_convert_option_to_result =
|
||||
Some(trait_ref.skip_binder().substs.type_at(1)) == none_error;
|
||||
let should_convert_result_to_option =
|
||||
Some(trait_ref.self_ty().skip_binder()) == none_error;
|
||||
if should_convert_option_to_result {
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_lo(),
|
||||
"consider converting the `Option<T>` into a `Result<T, _>` \
|
||||
using `Option::ok_or` or `Option::ok_or_else`",
|
||||
".ok_or_else(|| /* error value */)",
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else if should_convert_result_to_option {
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_lo(),
|
||||
"consider converting the `Result<T, _>` into an `Option<T>` \
|
||||
using `Result::ok`",
|
||||
".ok()",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
if let Some(ret_span) = self.return_type_span(&obligation) {
|
||||
err.span_label(
|
||||
ret_span,
|
||||
&format!(
|
||||
"expected `{}` because of this",
|
||||
trait_ref.skip_binder().self_ty()
|
||||
),
|
||||
);
|
||||
}
|
||||
if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) {
|
||||
err.span_label(
|
||||
ret_span,
|
||||
&format!(
|
||||
"expected `{}` because of this",
|
||||
trait_ref.skip_binder().self_ty()
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() {
|
||||
|
||||
@@ -1054,7 +1054,6 @@ pub trait UpperHex {
|
||||
pub trait Pointer {
|
||||
/// Formats the value using the given formatter.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_diagnostic_item = "pointer_trait_fmt"]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
|
||||
}
|
||||
|
||||
|
||||
@@ -338,7 +338,6 @@ macro_rules! debug_assert_ne {
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[stable(feature = "matches_macro", since = "1.42.0")]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "matches_macro")]
|
||||
macro_rules! matches {
|
||||
($expression:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => {
|
||||
match $expression {
|
||||
@@ -820,7 +819,6 @@ pub(crate) mod builtin {
|
||||
#[stable(feature = "compile_error_macro", since = "1.20.0")]
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "compile_error_macro")]
|
||||
macro_rules! compile_error {
|
||||
($msg:expr $(,)?) => {{ /* compiler built-in */ }};
|
||||
}
|
||||
@@ -944,7 +942,6 @@ macro_rules! format_args_nl {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "env_macro")]
|
||||
macro_rules! env {
|
||||
($name:expr $(,)?) => {{ /* compiler built-in */ }};
|
||||
($name:expr, $error_msg:expr $(,)?) => {{ /* compiler built-in */ }};
|
||||
@@ -973,7 +970,6 @@ macro_rules! env {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "option_env_macro")]
|
||||
macro_rules! option_env {
|
||||
($name:expr $(,)?) => {{ /* compiler built-in */ }};
|
||||
}
|
||||
@@ -1058,7 +1054,6 @@ macro_rules! concat_bytes {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "concat_macro")]
|
||||
macro_rules! concat {
|
||||
($($e:expr),* $(,)?) => {{ /* compiler built-in */ }};
|
||||
}
|
||||
@@ -1084,7 +1079,6 @@ macro_rules! concat {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "line_macro")]
|
||||
macro_rules! line {
|
||||
() => {
|
||||
/* compiler built-in */
|
||||
@@ -1124,7 +1118,6 @@ macro_rules! line {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "column_macro")]
|
||||
macro_rules! column {
|
||||
() => {
|
||||
/* compiler built-in */
|
||||
@@ -1150,7 +1143,6 @@ macro_rules! column {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "file_macro")]
|
||||
macro_rules! file {
|
||||
() => {
|
||||
/* compiler built-in */
|
||||
@@ -1175,7 +1167,6 @@ macro_rules! file {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "stringify_macro")]
|
||||
macro_rules! stringify {
|
||||
($($t:tt)*) => {
|
||||
/* compiler built-in */
|
||||
@@ -1282,7 +1273,6 @@ macro_rules! include_bytes {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "module_path_macro")]
|
||||
macro_rules! module_path {
|
||||
() => {
|
||||
/* compiler built-in */
|
||||
@@ -1316,7 +1306,6 @@ macro_rules! module_path {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "cfg_macro")]
|
||||
macro_rules! cfg {
|
||||
($($cfg:tt)*) => {
|
||||
/* compiler built-in */
|
||||
@@ -1367,7 +1356,6 @@ macro_rules! cfg {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "include_macro")]
|
||||
macro_rules! include {
|
||||
($file:expr $(,)?) => {{ /* compiler built-in */ }};
|
||||
}
|
||||
|
||||
@@ -321,7 +321,6 @@ impl $Ty {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let one = ", stringify!($Ty), "::new(1)?;")]
|
||||
@@ -356,7 +355,6 @@ pub const fn checked_add(self, other: $Int) -> Option<$Ty> {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let one = ", stringify!($Ty), "::new(1)?;")]
|
||||
@@ -391,8 +389,8 @@ pub const fn saturating_add(self, other: $Int) -> $Ty {
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonzero_ops)]
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let one = ", stringify!($Ty), "::new(1)?;")]
|
||||
@@ -420,7 +418,6 @@ pub const fn saturating_add(self, other: $Int) -> $Ty {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let two = ", stringify!($Ty), "::new(2)?;")]
|
||||
@@ -461,7 +458,6 @@ pub const fn checked_next_power_of_two(self) -> Option<$Ty> {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
#[doc = concat!("assert_eq!(", stringify!($Ty), "::new(7).unwrap().ilog2(), 2);")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($Ty), "::new(8).unwrap().ilog2(), 3);")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($Ty), "::new(9).unwrap().ilog2(), 3);")]
|
||||
@@ -486,7 +482,6 @@ pub const fn ilog2(self) -> u32 {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
#[doc = concat!("assert_eq!(", stringify!($Ty), "::new(99).unwrap().ilog10(), 1);")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($Ty), "::new(100).unwrap().ilog10(), 2);")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($Ty), "::new(101).unwrap().ilog10(), 2);")]
|
||||
@@ -526,7 +521,6 @@ impl $Ty {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
|
||||
@@ -556,7 +550,6 @@ pub const fn abs(self) -> $Ty {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
|
||||
@@ -591,7 +584,6 @@ pub const fn checked_abs(self) -> Option<$Ty> {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
|
||||
@@ -626,7 +618,6 @@ pub const fn overflowing_abs(self) -> ($Ty, bool) {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
|
||||
@@ -662,7 +653,6 @@ pub const fn saturating_abs(self) -> $Ty {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
|
||||
@@ -905,7 +895,6 @@ impl $Ty {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let two = ", stringify!($Ty), "::new(2)?;")]
|
||||
@@ -941,7 +930,6 @@ pub const fn checked_mul(self, other: $Ty) -> Option<$Ty> {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let two = ", stringify!($Ty), "::new(2)?;")]
|
||||
@@ -986,8 +974,8 @@ pub const fn saturating_mul(self, other: $Ty) -> $Ty {
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonzero_ops)]
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let two = ", stringify!($Ty), "::new(2)?;")]
|
||||
@@ -1014,7 +1002,6 @@ pub const fn saturating_mul(self, other: $Ty) -> $Ty {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let three = ", stringify!($Ty), "::new(3)?;")]
|
||||
@@ -1058,7 +1045,6 @@ pub const fn checked_pow(self, other: u32) -> Option<$Ty> {
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
/// # fn main() { test().unwrap(); }
|
||||
/// # fn test() -> Option<()> {
|
||||
#[doc = concat!("let three = ", stringify!($Ty), "::new(3)?;")]
|
||||
@@ -1162,8 +1148,8 @@ impl $Ty {
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonzero_min_max)]
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($Ty), "::MIN.get(), 1", stringify!($Int), ");")]
|
||||
/// ```
|
||||
#[unstable(feature = "nonzero_min_max", issue = "89065")]
|
||||
@@ -1177,8 +1163,8 @@ impl $Ty {
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonzero_min_max)]
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($Ty), "::MAX.get(), ", stringify!($Int), "::MAX);")]
|
||||
/// ```
|
||||
#[unstable(feature = "nonzero_min_max", issue = "89065")]
|
||||
@@ -1204,8 +1190,8 @@ impl $Ty {
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonzero_min_max)]
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($Ty), "::MIN.get(), ", stringify!($Int), "::MIN);")]
|
||||
/// ```
|
||||
#[unstable(feature = "nonzero_min_max", issue = "89065")]
|
||||
@@ -1223,8 +1209,8 @@ impl $Ty {
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonzero_min_max)]
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
///
|
||||
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
|
||||
#[doc = concat!("assert_eq!(", stringify!($Ty), "::MAX.get(), ", stringify!($Int), "::MAX);")]
|
||||
/// ```
|
||||
#[unstable(feature = "nonzero_min_max", issue = "89065")]
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
#[cfg(bootstrap)]
|
||||
#[lang = "fn"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_diagnostic_item = "Fn"]
|
||||
#[rustc_paren_sugar]
|
||||
#[rustc_on_unimplemented(
|
||||
on(
|
||||
@@ -138,7 +137,6 @@ pub trait Fn<Args>: FnMut<Args> {
|
||||
#[cfg(not(bootstrap))]
|
||||
#[lang = "fn"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_diagnostic_item = "Fn"]
|
||||
#[rustc_paren_sugar]
|
||||
#[rustc_on_unimplemented(
|
||||
on(
|
||||
@@ -227,7 +225,6 @@ pub trait Fn<Args: Tuple>: FnMut<Args> {
|
||||
#[cfg(bootstrap)]
|
||||
#[lang = "fn_mut"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_diagnostic_item = "FnMut"]
|
||||
#[rustc_paren_sugar]
|
||||
#[rustc_on_unimplemented(
|
||||
on(
|
||||
@@ -316,7 +313,6 @@ pub trait FnMut<Args>: FnOnce<Args> {
|
||||
#[cfg(not(bootstrap))]
|
||||
#[lang = "fn_mut"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_diagnostic_item = "FnMut"]
|
||||
#[rustc_paren_sugar]
|
||||
#[rustc_on_unimplemented(
|
||||
on(
|
||||
@@ -397,7 +393,6 @@ pub trait FnMut<Args: Tuple>: FnOnce<Args> {
|
||||
#[cfg(bootstrap)]
|
||||
#[lang = "fn_once"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_diagnostic_item = "FnOnce"]
|
||||
#[rustc_paren_sugar]
|
||||
#[rustc_on_unimplemented(
|
||||
on(
|
||||
@@ -483,7 +478,6 @@ pub trait FnOnce<Args> {
|
||||
#[cfg(not(bootstrap))]
|
||||
#[lang = "fn_once"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_diagnostic_item = "FnOnce"]
|
||||
#[rustc_paren_sugar]
|
||||
#[rustc_on_unimplemented(
|
||||
on(
|
||||
|
||||
@@ -80,7 +80,6 @@
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")]
|
||||
#[allow_internal_unstable(core_panic)]
|
||||
#[rustc_diagnostic_item = "unreachable_2021_macro"]
|
||||
#[rustc_macro_transparency = "semitransparent"]
|
||||
pub macro unreachable_2021 {
|
||||
() => (
|
||||
|
||||
@@ -16,7 +16,7 @@ panic_unwind = { path = "../panic_unwind", optional = true }
|
||||
panic_abort = { path = "../panic_abort" }
|
||||
core = { path = "../core" }
|
||||
libc = { version = "0.2.135", default-features = false, features = ['rustc-dep-of-std'] }
|
||||
compiler_builtins = { version = "0.1.73" }
|
||||
compiler_builtins = { version = "0.1.82" }
|
||||
profiler_builtins = { path = "../profiler_builtins", optional = true }
|
||||
unwind = { path = "../unwind" }
|
||||
hashbrown = { version = "0.12", default-features = false, features = ['rustc-dep-of-std'] }
|
||||
|
||||
@@ -73,7 +73,6 @@ pub enum IpAddr {
|
||||
/// assert!("0xcb.0x0.0x71.0x00".parse::<Ipv4Addr>().is_err()); // all octets are in hex
|
||||
/// ```
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "Ipv4Addr")]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Ipv4Addr {
|
||||
octets: [u8; 4],
|
||||
@@ -156,7 +155,6 @@ pub struct Ipv4Addr {
|
||||
/// assert_eq!(localhost.is_loopback(), true);
|
||||
/// ```
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "Ipv6Addr")]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Ipv6Addr {
|
||||
octets: [u8; 16],
|
||||
|
||||
@@ -0,0 +1,1041 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
The Rust toolchain test runner for Fuchsia.
|
||||
|
||||
For instructions on running the compiler test suite, see
|
||||
https://doc.rust-lang.org/stable/rustc/platform-support/fuchsia.html#aarch64-fuchsia-and-x86_64-fuchsia
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from dataclasses import dataclass
|
||||
import glob
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import ClassVar, List
|
||||
|
||||
|
||||
@dataclass
|
||||
class TestEnvironment:
|
||||
rust_dir: str
|
||||
sdk_dir: str
|
||||
target_arch: str
|
||||
package_server_pid: int = None
|
||||
emu_addr: str = None
|
||||
libstd_name: str = None
|
||||
libtest_name: str = None
|
||||
verbose: bool = False
|
||||
|
||||
@staticmethod
|
||||
def tmp_dir():
|
||||
tmp_dir = os.environ.get("TEST_TOOLCHAIN_TMP_DIR")
|
||||
if tmp_dir is not None:
|
||||
return os.path.abspath(tmp_dir)
|
||||
return os.path.join(os.path.dirname(__file__), "tmp~")
|
||||
|
||||
@classmethod
|
||||
def env_file_path(cls):
|
||||
return os.path.join(cls.tmp_dir(), "test_env.json")
|
||||
|
||||
@classmethod
|
||||
def from_args(cls, args):
|
||||
return cls(
|
||||
os.path.abspath(args.rust),
|
||||
os.path.abspath(args.sdk),
|
||||
args.target_arch,
|
||||
verbose=args.verbose,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def read_from_file(cls):
|
||||
with open(cls.env_file_path(), encoding="utf-8") as f:
|
||||
test_env = json.loads(f.read())
|
||||
return cls(
|
||||
test_env["rust_dir"],
|
||||
test_env["sdk_dir"],
|
||||
test_env["target_arch"],
|
||||
libstd_name=test_env["libstd_name"],
|
||||
libtest_name=test_env["libtest_name"],
|
||||
emu_addr=test_env["emu_addr"],
|
||||
package_server_pid=test_env["package_server_pid"],
|
||||
verbose=test_env["verbose"],
|
||||
)
|
||||
|
||||
def image_name(self):
|
||||
if self.target_arch == "x64":
|
||||
return "qemu-x64"
|
||||
if self.target_arch == "arm64":
|
||||
return "qemu-arm64"
|
||||
raise Exception(f"Unrecognized target architecture {self.target_arch}")
|
||||
|
||||
def write_to_file(self):
|
||||
with open(self.env_file_path(), "w", encoding="utf-8") as f:
|
||||
f.write(json.dumps(self.__dict__))
|
||||
|
||||
def ssh_dir(self):
|
||||
return os.path.join(self.tmp_dir(), "ssh")
|
||||
|
||||
def ssh_keyfile_path(self):
|
||||
return os.path.join(self.ssh_dir(), "fuchsia_ed25519")
|
||||
|
||||
def ssh_authfile_path(self):
|
||||
return os.path.join(self.ssh_dir(), "fuchsia_authorized_keys")
|
||||
|
||||
def vdl_output_path(self):
|
||||
return os.path.join(self.tmp_dir(), "vdl_output")
|
||||
|
||||
def package_server_log_path(self):
|
||||
return os.path.join(self.tmp_dir(), "package_server_log")
|
||||
|
||||
def emulator_log_path(self):
|
||||
return os.path.join(self.tmp_dir(), "emulator_log")
|
||||
|
||||
def packages_dir(self):
|
||||
return os.path.join(self.tmp_dir(), "packages")
|
||||
|
||||
def output_dir(self):
|
||||
return os.path.join(self.tmp_dir(), "output")
|
||||
|
||||
TEST_REPO_NAME: ClassVar[str] = "rust-testing"
|
||||
|
||||
def repo_dir(self):
|
||||
return os.path.join(self.tmp_dir(), self.TEST_REPO_NAME)
|
||||
|
||||
def rustlib_dir(self):
|
||||
if self.target_arch == "x64":
|
||||
return "x86_64-fuchsia"
|
||||
if self.target_arch == "arm64":
|
||||
return "aarch64-fuchsia"
|
||||
raise Exception(f"Unrecognized target architecture {self.target_arch}")
|
||||
|
||||
def libs_dir(self):
|
||||
return os.path.join(
|
||||
self.rust_dir,
|
||||
"lib",
|
||||
)
|
||||
|
||||
def rustlibs_dir(self):
|
||||
return os.path.join(
|
||||
self.libs_dir(),
|
||||
"rustlib",
|
||||
self.rustlib_dir(),
|
||||
"lib",
|
||||
)
|
||||
|
||||
def sdk_arch(self):
|
||||
machine = platform.machine()
|
||||
if machine == "x86_64":
|
||||
return "x64"
|
||||
if machine == "arm":
|
||||
return "a64"
|
||||
raise Exception(f"Unrecognized host architecture {machine}")
|
||||
|
||||
def tool_path(self, tool):
|
||||
return os.path.join(self.sdk_dir, "tools", self.sdk_arch(), tool)
|
||||
|
||||
def host_arch_triple(self):
|
||||
machine = platform.machine()
|
||||
if machine == "x86_64":
|
||||
return "x86_64-unknown-linux-gnu"
|
||||
if machine == "arm":
|
||||
return "aarch64-unknown-linux-gnu"
|
||||
raise Exception(f"Unrecognized host architecture {machine}")
|
||||
|
||||
def zxdb_script_path(self):
|
||||
return os.path.join(self.tmp_dir(), "zxdb_script")
|
||||
|
||||
def log_info(self, msg):
|
||||
print(msg)
|
||||
|
||||
def log_debug(self, msg):
|
||||
if self.verbose:
|
||||
print(msg)
|
||||
|
||||
def subprocess_output(self):
|
||||
if self.verbose:
|
||||
return sys.stdout
|
||||
return subprocess.DEVNULL
|
||||
|
||||
def ffx_daemon_log_path(self):
|
||||
return os.path.join(self.tmp_dir(), "ffx_daemon_log")
|
||||
|
||||
def ffx_isolate_dir(self):
|
||||
return os.path.join(self.tmp_dir(), "ffx_isolate")
|
||||
|
||||
def ffx_home_dir(self):
|
||||
return os.path.join(self.ffx_isolate_dir(), "user-home")
|
||||
|
||||
def ffx_tmp_dir(self):
|
||||
return os.path.join(self.ffx_isolate_dir(), "tmp")
|
||||
|
||||
def ffx_log_dir(self):
|
||||
return os.path.join(self.ffx_isolate_dir(), "log")
|
||||
|
||||
def ffx_user_config_dir(self):
|
||||
return os.path.join(self.ffx_xdg_config_home(), "Fuchsia", "ffx", "config")
|
||||
|
||||
def ffx_user_config_path(self):
|
||||
return os.path.join(self.ffx_user_config_dir(), "config.json")
|
||||
|
||||
def ffx_xdg_config_home(self):
|
||||
if platform.system() == "Darwin":
|
||||
return os.path.join(self.ffx_home_dir(), "Library", "Preferences")
|
||||
return os.path.join(self.ffx_home_dir(), ".local", "share")
|
||||
|
||||
def ffx_ascendd_path(self):
|
||||
return os.path.join(self.ffx_tmp_dir(), "ascendd")
|
||||
|
||||
def start_ffx_isolation(self):
|
||||
# Most of this is translated directly from ffx's isolate library
|
||||
os.mkdir(self.ffx_isolate_dir())
|
||||
os.mkdir(self.ffx_home_dir())
|
||||
os.mkdir(self.ffx_tmp_dir())
|
||||
os.mkdir(self.ffx_log_dir())
|
||||
|
||||
fuchsia_dir = os.path.join(self.ffx_home_dir(), ".fuchsia")
|
||||
os.mkdir(fuchsia_dir)
|
||||
|
||||
fuchsia_debug_dir = os.path.join(fuchsia_dir, "debug")
|
||||
os.mkdir(fuchsia_debug_dir)
|
||||
|
||||
metrics_dir = os.path.join(fuchsia_dir, "metrics")
|
||||
os.mkdir(metrics_dir)
|
||||
|
||||
analytics_path = os.path.join(metrics_dir, "analytics-status")
|
||||
with open(analytics_path, "w", encoding="utf-8") as analytics_file:
|
||||
print("0", file=analytics_file)
|
||||
|
||||
ffx_path = os.path.join(metrics_dir, "ffx")
|
||||
with open(ffx_path, "w", encoding="utf-8") as ffx_file:
|
||||
print("1", file=ffx_file)
|
||||
|
||||
os.makedirs(self.ffx_user_config_dir())
|
||||
|
||||
with open(
|
||||
self.ffx_user_config_path(), "w", encoding="utf-8"
|
||||
) as config_json_file:
|
||||
user_config_for_test = {
|
||||
"log": {
|
||||
"enabled": True,
|
||||
"dir": self.ffx_log_dir(),
|
||||
},
|
||||
"overnet": {
|
||||
"socket": self.ffx_ascendd_path(),
|
||||
},
|
||||
"ssh": {
|
||||
"pub": self.ssh_authfile_path(),
|
||||
"priv": self.ssh_keyfile_path(),
|
||||
},
|
||||
"test": {
|
||||
"is_isolated": True,
|
||||
"experimental_structured_output": True,
|
||||
},
|
||||
}
|
||||
print(json.dumps(user_config_for_test), file=config_json_file)
|
||||
|
||||
ffx_env_path = os.path.join(self.ffx_user_config_dir(), ".ffx_env")
|
||||
with open(ffx_env_path, "w", encoding="utf-8") as ffx_env_file:
|
||||
ffx_env_config_for_test = {
|
||||
"user": self.ffx_user_config_path(),
|
||||
"build": None,
|
||||
"global": None,
|
||||
}
|
||||
print(json.dumps(ffx_env_config_for_test), file=ffx_env_file)
|
||||
|
||||
# Start ffx daemon
|
||||
# We want this to be a long-running process that persists after the script finishes
|
||||
# pylint: disable=consider-using-with
|
||||
with open(
|
||||
self.ffx_daemon_log_path(), "w", encoding="utf-8"
|
||||
) as ffx_daemon_log_file:
|
||||
subprocess.Popen(
|
||||
[
|
||||
self.tool_path("ffx"),
|
||||
"--config",
|
||||
self.ffx_user_config_path(),
|
||||
"daemon",
|
||||
"start",
|
||||
],
|
||||
env=self.ffx_cmd_env(),
|
||||
stdout=ffx_daemon_log_file,
|
||||
stderr=ffx_daemon_log_file,
|
||||
)
|
||||
|
||||
def ffx_cmd_env(self):
|
||||
result = {
|
||||
"HOME": self.ffx_home_dir(),
|
||||
"XDG_CONFIG_HOME": self.ffx_xdg_config_home(),
|
||||
"ASCENDD": self.ffx_ascendd_path(),
|
||||
"FUCHSIA_SSH_KEY": self.ssh_keyfile_path(),
|
||||
# We want to use our own specified temp directory
|
||||
"TMP": self.tmp_dir(),
|
||||
"TEMP": self.tmp_dir(),
|
||||
"TMPDIR": self.tmp_dir(),
|
||||
"TEMPDIR": self.tmp_dir(),
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def stop_ffx_isolation(self):
|
||||
subprocess.check_call(
|
||||
[
|
||||
self.tool_path("ffx"),
|
||||
"--config",
|
||||
self.ffx_user_config_path(),
|
||||
"daemon",
|
||||
"stop",
|
||||
],
|
||||
env=self.ffx_cmd_env(),
|
||||
stdout=self.subprocess_output(),
|
||||
stderr=self.subprocess_output(),
|
||||
)
|
||||
|
||||
def start(self):
|
||||
"""Sets up the testing environment and prepares to run tests.
|
||||
|
||||
Args:
|
||||
args: The command-line arguments to this command.
|
||||
|
||||
During setup, this function will:
|
||||
- Locate necessary shared libraries
|
||||
- Create a new temp directory (this is where all temporary files are stored)
|
||||
- Start an emulator
|
||||
- Start an update server
|
||||
- Create a new package repo and register it with the emulator
|
||||
- Write test environment settings to a temporary file
|
||||
"""
|
||||
|
||||
# Initialize temp directory
|
||||
if not os.path.exists(self.tmp_dir()):
|
||||
os.mkdir(self.tmp_dir())
|
||||
elif len(os.listdir(self.tmp_dir())) != 0:
|
||||
raise Exception(f"Temp directory is not clean (in {self.tmp_dir()})")
|
||||
|
||||
os.mkdir(self.ssh_dir())
|
||||
os.mkdir(self.output_dir())
|
||||
|
||||
# Find libstd and libtest
|
||||
libstd_paths = glob.glob(os.path.join(self.rustlibs_dir(), "libstd-*.so"))
|
||||
libtest_paths = glob.glob(os.path.join(self.rustlibs_dir(), "libtest-*.so"))
|
||||
|
||||
if not libstd_paths:
|
||||
raise Exception(f"Failed to locate libstd (in {self.rustlibs_dir()})")
|
||||
|
||||
if not libtest_paths:
|
||||
raise Exception(f"Failed to locate libtest (in {self.rustlibs_dir()})")
|
||||
|
||||
self.libstd_name = os.path.basename(libstd_paths[0])
|
||||
self.libtest_name = os.path.basename(libtest_paths[0])
|
||||
|
||||
# Generate SSH keys for the emulator to use
|
||||
self.log_info("Generating SSH keys...")
|
||||
subprocess.check_call(
|
||||
[
|
||||
"ssh-keygen",
|
||||
"-N",
|
||||
"",
|
||||
"-t",
|
||||
"ed25519",
|
||||
"-f",
|
||||
self.ssh_keyfile_path(),
|
||||
"-C",
|
||||
"Generated by test_toolchain.py",
|
||||
],
|
||||
stdout=self.subprocess_output(),
|
||||
stderr=self.subprocess_output(),
|
||||
)
|
||||
authfile_contents = subprocess.check_output(
|
||||
[
|
||||
"ssh-keygen",
|
||||
"-y",
|
||||
"-f",
|
||||
self.ssh_keyfile_path(),
|
||||
],
|
||||
stderr=self.subprocess_output(),
|
||||
)
|
||||
with open(self.ssh_authfile_path(), "wb") as authfile:
|
||||
authfile.write(authfile_contents)
|
||||
|
||||
# Start ffx isolation
|
||||
self.log_info("Starting ffx isolation...")
|
||||
self.start_ffx_isolation()
|
||||
|
||||
# Start emulator (this will generate the vdl output)
|
||||
self.log_info("Starting emulator...")
|
||||
subprocess.check_call(
|
||||
[
|
||||
self.tool_path("fvdl"),
|
||||
"--sdk",
|
||||
"start",
|
||||
"--tuntap",
|
||||
"--headless",
|
||||
"--nointeractive",
|
||||
"--ssh",
|
||||
self.ssh_dir(),
|
||||
"--vdl-output",
|
||||
self.vdl_output_path(),
|
||||
"--emulator-log",
|
||||
self.emulator_log_path(),
|
||||
"--image-name",
|
||||
self.image_name(),
|
||||
],
|
||||
stdout=self.subprocess_output(),
|
||||
stderr=self.subprocess_output(),
|
||||
)
|
||||
|
||||
# Parse vdl output for relevant information
|
||||
with open(self.vdl_output_path(), encoding="utf-8") as f:
|
||||
vdl_content = f.read()
|
||||
matches = re.search(
|
||||
r'network_address:\s+"\[([0-9a-f]{1,4}:(:[0-9a-f]{1,4}){4}%qemu)\]"',
|
||||
vdl_content,
|
||||
)
|
||||
self.emu_addr = matches.group(1)
|
||||
|
||||
# Create new package repo
|
||||
self.log_info("Creating package repo...")
|
||||
subprocess.check_call(
|
||||
[
|
||||
self.tool_path("pm"),
|
||||
"newrepo",
|
||||
"-repo",
|
||||
self.repo_dir(),
|
||||
],
|
||||
stdout=self.subprocess_output(),
|
||||
stderr=self.subprocess_output(),
|
||||
)
|
||||
|
||||
# Start package server
|
||||
self.log_info("Starting package server...")
|
||||
with open(
|
||||
self.package_server_log_path(), "w", encoding="utf-8"
|
||||
) as package_server_log:
|
||||
# We want this to be a long-running process that persists after the script finishes
|
||||
# pylint: disable=consider-using-with
|
||||
self.package_server_pid = subprocess.Popen(
|
||||
[
|
||||
self.tool_path("pm"),
|
||||
"serve",
|
||||
"-vt",
|
||||
"-repo",
|
||||
self.repo_dir(),
|
||||
"-l",
|
||||
":8084",
|
||||
],
|
||||
stdout=package_server_log,
|
||||
stderr=package_server_log,
|
||||
).pid
|
||||
|
||||
# Register package server with emulator
|
||||
self.log_info("Registering package server...")
|
||||
ssh_client = subprocess.check_output(
|
||||
[
|
||||
"ssh",
|
||||
"-i",
|
||||
self.ssh_keyfile_path(),
|
||||
"-o",
|
||||
"StrictHostKeyChecking=accept-new",
|
||||
self.emu_addr,
|
||||
"-f",
|
||||
"echo $SSH_CLIENT",
|
||||
],
|
||||
text=True,
|
||||
)
|
||||
repo_addr = ssh_client.split()[0].replace("%", "%25")
|
||||
repo_url = f"http://[{repo_addr}]:8084/config.json"
|
||||
subprocess.check_call(
|
||||
[
|
||||
"ssh",
|
||||
"-i",
|
||||
self.ssh_keyfile_path(),
|
||||
"-o",
|
||||
"StrictHostKeyChecking=accept-new",
|
||||
self.emu_addr,
|
||||
"-f",
|
||||
f"pkgctl repo add url -f 1 -n {self.TEST_REPO_NAME} {repo_url}",
|
||||
],
|
||||
stdout=self.subprocess_output(),
|
||||
stderr=self.subprocess_output(),
|
||||
)
|
||||
|
||||
# Write to file
|
||||
self.write_to_file()
|
||||
|
||||
self.log_info("Success! Your environment is ready to run tests.")
|
||||
|
||||
# FIXME: shardify this
|
||||
# `facet` statement required for TCP testing via
|
||||
# protocol `fuchsia.posix.socket.Provider`. See
|
||||
# https://fuchsia.dev/fuchsia-src/development/testing/components/test_runner_framework?hl=en#legacy_non-hermetic_tests
|
||||
CML_TEMPLATE: ClassVar[
|
||||
str
|
||||
] = """
|
||||
{{
|
||||
program: {{
|
||||
runner: "elf_test_runner",
|
||||
binary: "bin/{exe_name}",
|
||||
forward_stderr_to: "log",
|
||||
forward_stdout_to: "log",
|
||||
environ: [{env_vars}
|
||||
]
|
||||
}},
|
||||
capabilities: [
|
||||
{{ protocol: "fuchsia.test.Suite" }},
|
||||
],
|
||||
expose: [
|
||||
{{
|
||||
protocol: "fuchsia.test.Suite",
|
||||
from: "self",
|
||||
}},
|
||||
],
|
||||
use: [
|
||||
{{ storage: "data", path: "/data" }},
|
||||
{{ protocol: [ "fuchsia.process.Launcher" ] }},
|
||||
{{ protocol: [ "fuchsia.posix.socket.Provider" ] }}
|
||||
],
|
||||
facets: {{
|
||||
"fuchsia.test": {{ type: "system" }},
|
||||
}},
|
||||
}}
|
||||
"""
|
||||
|
||||
MANIFEST_TEMPLATE = """
|
||||
meta/package={package_dir}/meta/package
|
||||
meta/{package_name}.cm={package_dir}/meta/{package_name}.cm
|
||||
bin/{exe_name}={bin_path}
|
||||
lib/{libstd_name}={rust_dir}/lib/rustlib/{rustlib_dir}/lib/{libstd_name}
|
||||
lib/{libtest_name}={rust_dir}/lib/rustlib/{rustlib_dir}/lib/{libtest_name}
|
||||
lib/ld.so.1={sdk_dir}/arch/{target_arch}/sysroot/lib/libc.so
|
||||
lib/libzircon.so={sdk_dir}/arch/{target_arch}/sysroot/lib/libzircon.so
|
||||
lib/libfdio.so={sdk_dir}/arch/{target_arch}/lib/libfdio.so
|
||||
"""
|
||||
|
||||
TEST_ENV_VARS: ClassVar[List[str]] = [
|
||||
"TEST_EXEC_ENV",
|
||||
"RUST_MIN_STACK",
|
||||
"RUST_BACKTRACE",
|
||||
"RUST_NEWRT",
|
||||
"RUST_LOG",
|
||||
"RUST_TEST_THREADS",
|
||||
]
|
||||
|
||||
def run(self, args):
|
||||
"""Runs the requested test in the testing environment.
|
||||
|
||||
Args:
|
||||
args: The command-line arguments to this command.
|
||||
Returns:
|
||||
The return code of the test (0 for success, else failure).
|
||||
|
||||
To run a test, this function will:
|
||||
- Create, compile, archive, and publish a test package
|
||||
- Run the test package on the emulator
|
||||
- Forward the test's stdout and stderr as this script's stdout and stderr
|
||||
"""
|
||||
|
||||
bin_path = os.path.abspath(args.bin_path)
|
||||
|
||||
# Build a unique, deterministic name for the test using the name of the
|
||||
# binary and the last 6 hex digits of the hash of the full path
|
||||
def path_checksum(path):
|
||||
m = hashlib.sha256()
|
||||
m.update(path.encode("utf-8"))
|
||||
return m.hexdigest()[0:6]
|
||||
|
||||
base_name = os.path.basename(os.path.dirname(args.bin_path))
|
||||
exe_name = base_name.lower().replace(".", "_")
|
||||
package_name = f"{exe_name}_{path_checksum(bin_path)}"
|
||||
|
||||
package_dir = os.path.join(self.packages_dir(), package_name)
|
||||
cml_path = os.path.join(package_dir, "meta", f"{package_name}.cml")
|
||||
cm_path = os.path.join(package_dir, "meta", f"{package_name}.cm")
|
||||
manifest_path = os.path.join(package_dir, f"{package_name}.manifest")
|
||||
far_path = os.path.join(package_dir, f"{package_name}-0.far")
|
||||
|
||||
shared_libs = args.shared_libs[: args.n]
|
||||
arguments = args.shared_libs[args.n :]
|
||||
|
||||
test_output_dir = os.path.join(self.output_dir(), package_name)
|
||||
|
||||
# Clean and create temporary output directory
|
||||
if os.path.exists(test_output_dir):
|
||||
shutil.rmtree(test_output_dir)
|
||||
|
||||
os.mkdir(test_output_dir)
|
||||
|
||||
# Open log file
|
||||
log_path = os.path.join(test_output_dir, "log")
|
||||
with open(log_path, "w", encoding="utf-8") as log_file:
|
||||
|
||||
def log(msg):
|
||||
print(msg, file=log_file)
|
||||
log_file.flush()
|
||||
|
||||
log(f"Bin path: {bin_path}")
|
||||
|
||||
log("Setting up package...")
|
||||
|
||||
# Set up package
|
||||
subprocess.check_call(
|
||||
[
|
||||
self.tool_path("pm"),
|
||||
"-o",
|
||||
package_dir,
|
||||
"-n",
|
||||
package_name,
|
||||
"init",
|
||||
],
|
||||
stdout=log_file,
|
||||
stderr=log_file,
|
||||
)
|
||||
|
||||
log("Writing CML...")
|
||||
|
||||
# Write and compile CML
|
||||
with open(cml_path, "w", encoding="utf-8") as cml:
|
||||
# Collect environment variables
|
||||
env_vars = ""
|
||||
for var_name in self.TEST_ENV_VARS:
|
||||
var_value = os.getenv(var_name)
|
||||
if var_value is not None:
|
||||
env_vars += f'\n "{var_name}={var_value}",'
|
||||
|
||||
# Default to no backtrace for test suite
|
||||
if os.getenv("RUST_BACKTRACE") == None:
|
||||
env_vars += f'\n "RUST_BACKTRACE=0",'
|
||||
|
||||
cml.write(
|
||||
self.CML_TEMPLATE.format(env_vars=env_vars, exe_name=exe_name)
|
||||
)
|
||||
|
||||
log("Compiling CML...")
|
||||
|
||||
subprocess.check_call(
|
||||
[
|
||||
self.tool_path("cmc"),
|
||||
"compile",
|
||||
cml_path,
|
||||
"--includepath",
|
||||
".",
|
||||
"--output",
|
||||
cm_path,
|
||||
],
|
||||
stdout=log_file,
|
||||
stderr=log_file,
|
||||
)
|
||||
|
||||
log("Writing manifest...")
|
||||
|
||||
# Write, build, and archive manifest
|
||||
with open(manifest_path, "w", encoding="utf-8") as manifest:
|
||||
manifest.write(
|
||||
self.MANIFEST_TEMPLATE.format(
|
||||
bin_path=bin_path,
|
||||
exe_name=exe_name,
|
||||
package_dir=package_dir,
|
||||
package_name=package_name,
|
||||
rust_dir=self.rust_dir,
|
||||
rustlib_dir=self.rustlib_dir(),
|
||||
sdk_dir=self.sdk_dir,
|
||||
libstd_name=self.libstd_name,
|
||||
libtest_name=self.libtest_name,
|
||||
target_arch=self.target_arch,
|
||||
)
|
||||
)
|
||||
for shared_lib in shared_libs:
|
||||
manifest.write(f"lib/{os.path.basename(shared_lib)}={shared_lib}\n")
|
||||
|
||||
log("Compiling and archiving manifest...")
|
||||
|
||||
subprocess.check_call(
|
||||
[
|
||||
self.tool_path("pm"),
|
||||
"-o",
|
||||
package_dir,
|
||||
"-m",
|
||||
manifest_path,
|
||||
"build",
|
||||
],
|
||||
stdout=log_file,
|
||||
stderr=log_file,
|
||||
)
|
||||
subprocess.check_call(
|
||||
[
|
||||
self.tool_path("pm"),
|
||||
"-o",
|
||||
package_dir,
|
||||
"-m",
|
||||
manifest_path,
|
||||
"archive",
|
||||
],
|
||||
stdout=log_file,
|
||||
stderr=log_file,
|
||||
)
|
||||
|
||||
log("Publishing package to repo...")
|
||||
|
||||
# Publish package to repo
|
||||
subprocess.check_call(
|
||||
[
|
||||
self.tool_path("pm"),
|
||||
"publish",
|
||||
"-a",
|
||||
"-repo",
|
||||
self.repo_dir(),
|
||||
"-f",
|
||||
far_path,
|
||||
],
|
||||
stdout=log_file,
|
||||
stderr=log_file,
|
||||
)
|
||||
|
||||
log("Running ffx test...")
|
||||
|
||||
# Run test on emulator
|
||||
subprocess.run(
|
||||
[
|
||||
self.tool_path("ffx"),
|
||||
"--config",
|
||||
self.ffx_user_config_path(),
|
||||
"test",
|
||||
"run",
|
||||
f"fuchsia-pkg://{self.TEST_REPO_NAME}/{package_name}#meta/{package_name}.cm",
|
||||
"--min-severity-logs",
|
||||
"TRACE",
|
||||
"--output-directory",
|
||||
test_output_dir,
|
||||
"--",
|
||||
]
|
||||
+ arguments,
|
||||
env=self.ffx_cmd_env(),
|
||||
check=False,
|
||||
stdout=log_file,
|
||||
stderr=log_file,
|
||||
)
|
||||
|
||||
log("Reporting test suite output...")
|
||||
|
||||
# Read test suite output
|
||||
run_summary_path = os.path.join(test_output_dir, "run_summary.json")
|
||||
if os.path.exists(run_summary_path):
|
||||
with open(run_summary_path, encoding="utf-8") as f:
|
||||
run_summary = json.loads(f.read())
|
||||
|
||||
suite = run_summary["data"]["suites"][0]
|
||||
case = suite["cases"][0]
|
||||
|
||||
return_code = 0 if case["outcome"] == "PASSED" else 1
|
||||
|
||||
artifacts = case["artifacts"]
|
||||
artifact_dir = case["artifact_dir"]
|
||||
stdout_path = None
|
||||
stderr_path = None
|
||||
|
||||
for path, artifact in artifacts.items():
|
||||
artifact_path = os.path.join(test_output_dir, artifact_dir, path)
|
||||
artifact_type = artifact["artifact_type"]
|
||||
|
||||
if artifact_type == "STDERR":
|
||||
stderr_path = artifact_path
|
||||
elif artifact_type == "STDOUT":
|
||||
stdout_path = artifact_path
|
||||
|
||||
if stdout_path is not None and os.path.exists(stdout_path):
|
||||
with open(stdout_path, encoding="utf-8") as f:
|
||||
print(f.read(), file=sys.stdout, end="")
|
||||
|
||||
if stderr_path is not None and os.path.exists(stderr_path):
|
||||
with open(stderr_path, encoding="utf-8") as f:
|
||||
print(f.read(), file=sys.stderr, end="")
|
||||
else:
|
||||
log("Failed to open test run summary")
|
||||
return_code = 254
|
||||
|
||||
log("Done!")
|
||||
|
||||
return return_code
|
||||
|
||||
def stop(self):
|
||||
"""Shuts down and cleans up the testing environment.
|
||||
|
||||
Args:
|
||||
args: The command-line arguments to this command.
|
||||
Returns:
|
||||
The return code of the test (0 for success, else failure).
|
||||
|
||||
During cleanup, this function will stop the emulator, package server, and
|
||||
update server, then delete all temporary files. If an error is encountered
|
||||
while stopping any running processes, the temporary files will not be deleted.
|
||||
Passing --delete-tmp will force the process to delete the files anyway.
|
||||
"""
|
||||
|
||||
self.log_debug("Reporting logs...")
|
||||
|
||||
# Print test log files
|
||||
for test_dir in os.listdir(self.output_dir()):
|
||||
log_path = os.path.join(self.output_dir(), test_dir, "log")
|
||||
self.log_debug(f"\n---- Logs for test '{test_dir}' ----\n")
|
||||
if os.path.exists(log_path):
|
||||
with open(log_path, encoding="utf-8") as log:
|
||||
self.log_debug(log.read())
|
||||
else:
|
||||
self.log_debug("No logs found")
|
||||
|
||||
# Print the emulator log
|
||||
self.log_debug("\n---- Emulator logs ----\n")
|
||||
if os.path.exists(self.emulator_log_path()):
|
||||
with open(self.emulator_log_path(), encoding="utf-8") as log:
|
||||
self.log_debug(log.read())
|
||||
else:
|
||||
self.log_debug("No emulator logs found")
|
||||
|
||||
# Print the package server log
|
||||
self.log_debug("\n---- Package server log ----\n")
|
||||
if os.path.exists(self.package_server_log_path()):
|
||||
with open(self.package_server_log_path(), encoding="utf-8") as log:
|
||||
self.log_debug(log.read())
|
||||
else:
|
||||
self.log_debug("No package server log found")
|
||||
|
||||
# Print the ffx daemon log
|
||||
self.log_debug("\n---- ffx daemon log ----\n")
|
||||
if os.path.exists(self.ffx_daemon_log_path()):
|
||||
with open(self.ffx_daemon_log_path(), encoding="utf-8") as log:
|
||||
self.log_debug(log.read())
|
||||
else:
|
||||
self.log_debug("No ffx daemon log found")
|
||||
|
||||
# Stop package server
|
||||
self.log_info("Stopping package server...")
|
||||
os.kill(self.package_server_pid, signal.SIGTERM)
|
||||
|
||||
# Shut down the emulator
|
||||
self.log_info("Stopping emulator...")
|
||||
subprocess.check_call(
|
||||
[
|
||||
self.tool_path("fvdl"),
|
||||
"--sdk",
|
||||
"kill",
|
||||
"--launched-proto",
|
||||
self.vdl_output_path(),
|
||||
],
|
||||
stdout=self.subprocess_output(),
|
||||
stderr=self.subprocess_output(),
|
||||
)
|
||||
|
||||
# Stop ffx isolation
|
||||
self.log_info("Stopping ffx isolation...")
|
||||
self.stop_ffx_isolation()
|
||||
|
||||
def delete_tmp(self):
|
||||
# Remove temporary files
|
||||
self.log_info("Deleting temporary files...")
|
||||
shutil.rmtree(self.tmp_dir(), ignore_errors=True)
|
||||
|
||||
def debug(self, args):
|
||||
command = [
|
||||
self.tool_path("ffx"),
|
||||
"--config",
|
||||
self.ffx_user_config_path(),
|
||||
"debug",
|
||||
"connect",
|
||||
"--",
|
||||
"--build-id-dir",
|
||||
os.path.join(self.sdk_dir, ".build-id"),
|
||||
"--build-id-dir",
|
||||
os.path.join(self.libs_dir(), ".build-id"),
|
||||
]
|
||||
|
||||
# Add rust source if it's available
|
||||
if args.rust_src is not None:
|
||||
command += [
|
||||
"--build-dir",
|
||||
args.rust_src,
|
||||
]
|
||||
|
||||
# Add fuchsia source if it's available
|
||||
if args.fuchsia_src is not None:
|
||||
command += [
|
||||
"--build-dir",
|
||||
os.path.join(args.fuchsia_src, "out", "default"),
|
||||
]
|
||||
|
||||
# Load debug symbols for the test binary and automatically attach
|
||||
if args.test is not None:
|
||||
if args.rust_src is None:
|
||||
raise Exception(
|
||||
"A Rust source path is required with the `test` argument"
|
||||
)
|
||||
|
||||
test_name = os.path.splitext(os.path.basename(args.test))[0]
|
||||
|
||||
build_dir = os.path.join(
|
||||
args.rust_src,
|
||||
"fuchsia-build",
|
||||
self.host_arch_triple(),
|
||||
)
|
||||
test_dir = os.path.join(
|
||||
build_dir,
|
||||
"test",
|
||||
os.path.dirname(args.test),
|
||||
test_name,
|
||||
)
|
||||
|
||||
with open(self.zxdb_script_path(), mode="w", encoding="utf-8") as f:
|
||||
print(f"attach {test_name[:31]}", file=f)
|
||||
|
||||
command += [
|
||||
"--symbol-path",
|
||||
test_dir,
|
||||
"-S",
|
||||
self.zxdb_script_path(),
|
||||
]
|
||||
|
||||
# Add any other zxdb arguments the user passed
|
||||
if args.zxdb_args is not None:
|
||||
command += args.zxdb_args
|
||||
|
||||
# Connect to the running emulator with zxdb
|
||||
subprocess.run(command, env=self.ffx_cmd_env(), check=False)
|
||||
|
||||
|
||||
def start(args):
|
||||
test_env = TestEnvironment.from_args(args)
|
||||
test_env.start()
|
||||
return 0
|
||||
|
||||
|
||||
def run(args):
|
||||
test_env = TestEnvironment.read_from_file()
|
||||
return test_env.run(args)
|
||||
|
||||
|
||||
def stop(args):
|
||||
test_env = TestEnvironment.read_from_file()
|
||||
test_env.stop()
|
||||
if not args.no_delete:
|
||||
test_env.delete_tmp()
|
||||
return 0
|
||||
|
||||
|
||||
def delete_tmp(args):
|
||||
del args
|
||||
test_env = TestEnvironment.read_from_file()
|
||||
test_env.delete_tmp()
|
||||
return 0
|
||||
|
||||
|
||||
def debug(args):
|
||||
test_env = TestEnvironment.read_from_file()
|
||||
test_env.debug(args)
|
||||
return 0
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
def print_help(args):
|
||||
del args
|
||||
parser.print_help()
|
||||
return 0
|
||||
|
||||
parser.set_defaults(func=print_help)
|
||||
|
||||
subparsers = parser.add_subparsers(help="valid sub-commands")
|
||||
|
||||
start_parser = subparsers.add_parser(
|
||||
"start", help="initializes the testing environment"
|
||||
)
|
||||
start_parser.add_argument(
|
||||
"--rust",
|
||||
help="the directory of the installed Rust compiler for Fuchsia",
|
||||
required=True,
|
||||
)
|
||||
start_parser.add_argument(
|
||||
"--sdk",
|
||||
help="the directory of the fuchsia SDK",
|
||||
required=True,
|
||||
)
|
||||
start_parser.add_argument(
|
||||
"--verbose",
|
||||
help="prints more output from executed processes",
|
||||
action="store_true",
|
||||
)
|
||||
start_parser.add_argument(
|
||||
"--target-arch",
|
||||
help="the architecture of the image to test",
|
||||
required=True,
|
||||
)
|
||||
start_parser.set_defaults(func=start)
|
||||
|
||||
run_parser = subparsers.add_parser(
|
||||
"run", help="run a test in the testing environment"
|
||||
)
|
||||
run_parser.add_argument(
|
||||
"n", help="the number of shared libs passed along with the executable", type=int
|
||||
)
|
||||
run_parser.add_argument("bin_path", help="path to the binary to run")
|
||||
run_parser.add_argument(
|
||||
"shared_libs",
|
||||
help="the shared libs passed along with the binary",
|
||||
nargs=argparse.REMAINDER,
|
||||
)
|
||||
run_parser.set_defaults(func=run)
|
||||
|
||||
stop_parser = subparsers.add_parser(
|
||||
"stop", help="shuts down and cleans up the testing environment"
|
||||
)
|
||||
stop_parser.add_argument(
|
||||
"--no-delete",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="don't delete temporary files after stopping",
|
||||
)
|
||||
stop_parser.set_defaults(func=stop)
|
||||
|
||||
delete_parser = subparsers.add_parser(
|
||||
"delete-tmp",
|
||||
help="deletes temporary files after the testing environment has been manually cleaned up",
|
||||
)
|
||||
delete_parser.set_defaults(func=delete_tmp)
|
||||
|
||||
debug_parser = subparsers.add_parser(
|
||||
"debug",
|
||||
help="connect to the active testing environment with zxdb",
|
||||
)
|
||||
debug_parser.add_argument(
|
||||
"--rust-src",
|
||||
default=None,
|
||||
help="the path to the Rust source being tested",
|
||||
)
|
||||
debug_parser.add_argument(
|
||||
"--fuchsia-src",
|
||||
default=None,
|
||||
help="the path to the Fuchsia source",
|
||||
)
|
||||
debug_parser.add_argument(
|
||||
"--test",
|
||||
default=None,
|
||||
help="the path to the test to debug (e.g. ui/box/new.rs)",
|
||||
)
|
||||
debug_parser.add_argument(
|
||||
"zxdb_args",
|
||||
default=None,
|
||||
nargs=argparse.REMAINDER,
|
||||
help="any additional arguments to pass to zxdb",
|
||||
)
|
||||
debug_parser.set_defaults(func=debug)
|
||||
|
||||
args = parser.parse_args()
|
||||
return args.func(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -641,8 +641,60 @@ available on the [Fuchsia devsite].
|
||||
|
||||
### Running the compiler test suite
|
||||
|
||||
Running the Rust test suite on Fuchsia is [not currently supported], but work is
|
||||
underway to enable it.
|
||||
Pre-requisites for running the Rust test suite on Fuchsia are:
|
||||
1. Checkout of Rust source.
|
||||
1. Setup of `config-env.sh` and `config.toml` from "[Targeting Fuchsia with a compiler built from source](#targeting-fuchsia-with-a-compiler-built-from-source)".
|
||||
1. Download of the Fuchsia SDK. Minimum supported SDK version is [9.20220726.1.1](https://chrome-infra-packages.appspot.com/p/fuchsia/sdk/core/linux-amd64/+/version:9.20220726.1.1)
|
||||
|
||||
Interfacing with the Fuchsia emulator is handled by our test runner script located
|
||||
at `${RUST_SRC_PATH}/src/ci/docker/scripts/fuchsia-test-runner.py`.
|
||||
|
||||
We start by activating our Fuchsia test environment. From a terminal:
|
||||
|
||||
**Issue command from ${RUST_SRC_PATH}**
|
||||
```sh
|
||||
src/ci/docker/scripts/fuchsia-test-runner.py start
|
||||
--rust .
|
||||
--sdk ${SDK_PATH}
|
||||
--target-arch {x64,arm64}
|
||||
```
|
||||
|
||||
Next, for ease of commands, we copy `config-env.sh` and `config.toml` into our Rust source
|
||||
code path, `${RUST_SRC_PATH}`.
|
||||
|
||||
From there, we utilize `x.py` to run our tests, using the test runner script to
|
||||
run the tests on our emulator. To run the full `src/test/ui` test suite:
|
||||
|
||||
**Run from ${RUST_SRC_PATH}**
|
||||
```sh
|
||||
( \
|
||||
source config-env.sh && \
|
||||
./x.py \
|
||||
--config config.toml \
|
||||
--stage=2 \
|
||||
test src/test/ui \
|
||||
--target x86_64-fuchsia \
|
||||
--run=always --jobs 1 \
|
||||
--test-args --target-rustcflags -L \
|
||||
--test-args --target-rustcflags ${SDK_PATH}/arch/{x64|arm64}/sysroot/lib \
|
||||
--test-args --target-rustcflags -L \
|
||||
--test-args --target-rustcflags ${SDK_PATH}/arch/{x64|arm64}/lib \
|
||||
--test-args --target-rustcflags -Cpanic=abort \
|
||||
--test-args --target-rustcflags -Zpanic_abort_tests \
|
||||
--test-args --remote-test-client \
|
||||
--test-args src/ci/docker/scripts/fuchsia-test-runner.py \
|
||||
)
|
||||
```
|
||||
|
||||
*Note: The test suite cannot be run in parallel at the moment, so `x.py`
|
||||
must be run with `--jobs 1` to ensure only one test runs at a time.*
|
||||
|
||||
When finished, stop the test environment:
|
||||
|
||||
**Issue command from ${RUST_SRC_PATH}**
|
||||
```sh
|
||||
src/ci/docker/scripts/fuchsia-test-runner.py stop
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
|
||||
@@ -1262,10 +1262,6 @@ h3.variant {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
:target > code, :target > .code-header {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:target {
|
||||
padding-right: 3px;
|
||||
background-color: var(--target-background-color);
|
||||
|
||||
@@ -1491,6 +1491,7 @@ function initSearch(rawSearchIndex) {
|
||||
const target = searchState.focusedByTab[searchState.currentTab] ||
|
||||
document.querySelectorAll(".search-results.active a").item(0) ||
|
||||
document.querySelectorAll("#titles > button").item(searchState.currentTab);
|
||||
searchState.focusedByTab[searchState.currentTab] = null;
|
||||
if (target) {
|
||||
target.focus();
|
||||
}
|
||||
|
||||
@@ -19,9 +19,13 @@ fn new(filename: &str, bytes: &'static [u8]) -> StaticFile {
|
||||
}
|
||||
|
||||
pub(crate) fn minified(&self) -> Vec<u8> {
|
||||
if self.filename.ends_with(".css") {
|
||||
let extension = match self.filename.extension() {
|
||||
Some(e) => e,
|
||||
None => return self.bytes.to_owned(),
|
||||
};
|
||||
if extension == "css" {
|
||||
minifier::css::minify(str::from_utf8(self.bytes).unwrap()).unwrap().to_string().into()
|
||||
} else if self.filename.ends_with(".js") {
|
||||
} else if extension == "js" {
|
||||
minifier::js::minify(str::from_utf8(self.bytes).unwrap()).to_string().into()
|
||||
} else {
|
||||
self.bytes.to_owned()
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Checks that the search tab results work correctly with function signature syntax
|
||||
// First, try a search-by-name
|
||||
goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
write: (".search-input", "Foo")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
|
||||
// Now use the keyboard commands to switch to the third result.
|
||||
press-key: "ArrowDown"
|
||||
press-key: "ArrowDown"
|
||||
press-key: "ArrowDown"
|
||||
assert: ".search-results.active > a:focus:nth-of-type(3)"
|
||||
|
||||
// Now switch to the second tab, then back to the first one, then arrow back up.
|
||||
press-key: "ArrowRight"
|
||||
assert: ".search-results.active:nth-of-type(2) > a:focus:nth-of-type(1)"
|
||||
press-key: "ArrowLeft"
|
||||
assert: ".search-results.active:nth-of-type(1) > a:focus:nth-of-type(3)"
|
||||
press-key: "ArrowUp"
|
||||
assert: ".search-results.active > a:focus:nth-of-type(2)"
|
||||
press-key: "ArrowUp"
|
||||
assert: ".search-results.active > a:focus:nth-of-type(1)"
|
||||
press-key: "ArrowUp"
|
||||
assert: ".search-input:focus"
|
||||
press-key: "ArrowDown"
|
||||
assert: ".search-results.active > a:focus:nth-of-type(1)"
|
||||
@@ -0,0 +1,13 @@
|
||||
trait A<Y, N> {
|
||||
type B;
|
||||
}
|
||||
type MaybeBox<T> = <T as A<T, Box<T>>>::B;
|
||||
struct P {
|
||||
t: MaybeBox<P>
|
||||
}
|
||||
impl<Y, N> A<Y, N> for P {
|
||||
type B = N;
|
||||
}
|
||||
fn main() {
|
||||
let t: MaybeBox<P>;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
1_u32.MAX();
|
||||
//~^ ERROR no method named `MAX` found for type `u32` in the current scope
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
error[E0599]: no method named `MAX` found for type `u32` in the current scope
|
||||
--> $DIR/dont-suggest-ufcs-for-const.rs:2:11
|
||||
|
|
||||
LL | 1_u32.MAX();
|
||||
| ------^^^--
|
||||
| | |
|
||||
| | this is an associated function, not a method
|
||||
| help: use associated function syntax instead: `u32::MAX()`
|
||||
|
|
||||
= note: found the following associated functions; to be used as methods, functions must have a `self` parameter
|
||||
= note: the candidate is defined in an impl for the type `u32`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
||||
@@ -0,0 +1,15 @@
|
||||
// run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
struct Foo<T>(T);
|
||||
|
||||
impl<T> Foo<T> {
|
||||
fn test() -> i32 { 1 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = Box::new(Foo(1i32));
|
||||
Foo::<i32>::test();
|
||||
//~^ ERROR no method named `test` found for struct `Box<Foo<i32>>` in the current scope
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
struct Foo<T>(T);
|
||||
|
||||
impl<T> Foo<T> {
|
||||
fn test() -> i32 { 1 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = Box::new(Foo(1i32));
|
||||
x.test();
|
||||
//~^ ERROR no method named `test` found for struct `Box<Foo<i32>>` in the current scope
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
error[E0599]: no method named `test` found for struct `Box<Foo<i32>>` in the current scope
|
||||
--> $DIR/suggest-assoc-fn-call-deref.rs:13:7
|
||||
|
|
||||
LL | x.test();
|
||||
| --^^^^--
|
||||
| | |
|
||||
| | this is an associated function, not a method
|
||||
| help: use associated function syntax instead: `Foo::<i32>::test()`
|
||||
|
|
||||
= note: found the following associated functions; to be used as methods, functions must have a `self` parameter
|
||||
note: the candidate is defined in an impl for the type `Foo<T>`
|
||||
--> $DIR/suggest-assoc-fn-call-deref.rs:8:5
|
||||
|
|
||||
LL | fn test() -> i32 { 1 }
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
||||
@@ -199,12 +199,12 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<
|
||||
pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool {
|
||||
let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false };
|
||||
matches!(
|
||||
name.as_str(),
|
||||
"core_panic_macro"
|
||||
| "std_panic_macro"
|
||||
| "core_panic_2015_macro"
|
||||
| "std_panic_2015_macro"
|
||||
| "core_panic_2021_macro"
|
||||
name,
|
||||
sym::core_panic_macro
|
||||
| sym::std_panic_macro
|
||||
| sym::core_panic_2015_macro
|
||||
| sym::std_panic_2015_macro
|
||||
| sym::core_panic_2021_macro
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user