Auto merge of #139023 - jhpratt:rollup-4ou6ei4, r=jhpratt
Rollup of 7 pull requests Successful merges: - #138844 (expand: Leave traces when expanding `cfg` attributes) - #138926 (Remove `kw::Empty` uses from `rustc_middle`.) - #138989 (Clean up a few things in rustc_hir_analysis::check::region) - #138999 (Report compiletest pass mode if forced) - #139014 (Improve suggest construct with literal syntax instead of calling) - #139015 (Remove unneeded LLVM CI test assertions) - #139016 (Add job duration changes to post-merge analysis report) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
3f5502370b
44 changed files with 428 additions and 250 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -69,6 +69,8 @@ jobs:
|
|||
env:
|
||||
CI_JOB_NAME: ${{ matrix.name }}
|
||||
CI_JOB_DOC_URL: ${{ matrix.doc_url }}
|
||||
GITHUB_WORKFLOW_RUN_ID: ${{ github.run_id }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
|
||||
# commit of PR sha or commit sha. `GITHUB_SHA` is not accurate for PRs.
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
|
|
|||
|
|
@ -334,8 +334,7 @@ impl<'a> AstValidator<'a> {
|
|||
.filter(|attr| {
|
||||
let arr = [
|
||||
sym::allow,
|
||||
sym::cfg,
|
||||
sym::cfg_attr,
|
||||
sym::cfg_trace,
|
||||
sym::cfg_attr_trace,
|
||||
sym::deny,
|
||||
sym::expect,
|
||||
|
|
|
|||
|
|
@ -593,7 +593,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
}
|
||||
|
||||
fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) -> bool {
|
||||
if attr.has_name(sym::cfg_attr_trace) {
|
||||
if attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace) {
|
||||
// It's not a valid identifier, so avoid printing it
|
||||
// to keep the printed code reasonably parse-able.
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -156,6 +156,19 @@ pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn attr_into_trace(mut attr: Attribute, trace_name: Symbol) -> Attribute {
|
||||
match &mut attr.kind {
|
||||
AttrKind::Normal(normal) => {
|
||||
let NormalAttr { item, tokens } = &mut **normal;
|
||||
item.path.segments[0].ident.name = trace_name;
|
||||
// This makes the trace attributes unobservable to token-based proc macros.
|
||||
*tokens = Some(LazyAttrTokenStream::new(AttrTokenStream::default()));
|
||||
}
|
||||
AttrKind::DocComment(..) => unreachable!(),
|
||||
}
|
||||
attr
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! configure {
|
||||
($this:ident, $node:ident) => {
|
||||
|
|
@ -280,16 +293,7 @@ impl<'a> StripUnconfigured<'a> {
|
|||
|
||||
// A trace attribute left in AST in place of the original `cfg_attr` attribute.
|
||||
// It can later be used by lints or other diagnostics.
|
||||
let mut trace_attr = cfg_attr.clone();
|
||||
match &mut trace_attr.kind {
|
||||
AttrKind::Normal(normal) => {
|
||||
let NormalAttr { item, tokens } = &mut **normal;
|
||||
item.path.segments[0].ident.name = sym::cfg_attr_trace;
|
||||
// This makes the trace attributes unobservable to token-based proc macros.
|
||||
*tokens = Some(LazyAttrTokenStream::new(AttrTokenStream::default()));
|
||||
}
|
||||
AttrKind::DocComment(..) => unreachable!(),
|
||||
}
|
||||
let trace_attr = attr_into_trace(cfg_attr.clone(), sym::cfg_attr_trace);
|
||||
|
||||
let Some((cfg_predicate, expanded_attrs)) =
|
||||
rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, sym};
|
|||
use smallvec::SmallVec;
|
||||
|
||||
use crate::base::*;
|
||||
use crate::config::StripUnconfigured;
|
||||
use crate::config::{StripUnconfigured, attr_into_trace};
|
||||
use crate::errors::{
|
||||
EmptyDelegationMac, GlobDelegationOutsideImpls, GlobDelegationTraitlessQpath, IncompleteParse,
|
||||
RecursionLimitReached, RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue,
|
||||
|
|
@ -2003,7 +2003,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||
let attr_name = attr.ident().unwrap().name;
|
||||
// `#[cfg]` and `#[cfg_attr]` are special - they are
|
||||
// eagerly evaluated.
|
||||
if attr_name != sym::cfg && attr_name != sym::cfg_attr_trace {
|
||||
if attr_name != sym::cfg_trace && attr_name != sym::cfg_attr_trace {
|
||||
self.cx.sess.psess.buffer_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
attr.span,
|
||||
|
|
@ -2027,11 +2027,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||
) -> (bool, Option<ast::MetaItem>) {
|
||||
let (res, meta_item) = self.cfg().cfg_true(&attr);
|
||||
if res {
|
||||
// FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion,
|
||||
// and some tools like rustdoc and clippy rely on that. Find a way to remove them
|
||||
// while keeping the tools working.
|
||||
self.cx.expanded_inert_attrs.mark(&attr);
|
||||
node.visit_attrs(|attrs| attrs.insert(pos, attr));
|
||||
// A trace attribute left in AST in place of the original `cfg` attribute.
|
||||
// It can later be used by lints or other diagnostics.
|
||||
let trace_attr = attr_into_trace(attr, sym::cfg_trace);
|
||||
node.visit_attrs(|attrs| attrs.insert(pos, trace_attr));
|
||||
}
|
||||
|
||||
(res, meta_item)
|
||||
|
|
|
|||
|
|
@ -760,10 +760,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
template!(Word, List: r#""...""#), DuplicatesOk,
|
||||
EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
|
||||
),
|
||||
// Trace that is left when a `cfg_attr` attribute is expanded.
|
||||
// The attribute is not gated, to avoid stability errors, but it cannot be used in stable or
|
||||
// unstable code directly because `sym::cfg_attr_trace` is not a valid identifier, it can only
|
||||
// be generated by the compiler.
|
||||
// Traces that are left when `cfg` and `cfg_attr` attributes are expanded.
|
||||
// The attributes are not gated, to avoid stability errors, but they cannot be used in stable
|
||||
// or unstable code directly because `sym::cfg_(attr_)trace` are not valid identifiers, they
|
||||
// can only be generated by the compiler.
|
||||
ungated!(
|
||||
cfg_trace, Normal, template!(Word /* irrelevant */), DuplicatesOk,
|
||||
EncodeCrossCrate::No
|
||||
),
|
||||
ungated!(
|
||||
cfg_attr_trace, Normal, template!(Word /* irrelevant */), DuplicatesOk,
|
||||
EncodeCrossCrate::No
|
||||
|
|
|
|||
|
|
@ -25,12 +25,18 @@ use tracing::debug;
|
|||
struct Context {
|
||||
/// The scope that contains any new variables declared, plus its depth in
|
||||
/// the scope tree.
|
||||
var_parent: Option<(Scope, ScopeDepth)>,
|
||||
var_parent: Option<Scope>,
|
||||
|
||||
/// Region parent of expressions, etc., plus its depth in the scope tree.
|
||||
parent: Option<(Scope, ScopeDepth)>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
fn set_var_parent(&mut self) {
|
||||
self.var_parent = self.parent.map(|(p, _)| p);
|
||||
}
|
||||
}
|
||||
|
||||
struct ScopeResolutionVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
||||
|
|
@ -78,7 +84,7 @@ fn record_var_lifetime(visitor: &mut ScopeResolutionVisitor<'_>, var_id: hir::It
|
|||
//
|
||||
// extern fn isalnum(c: c_int) -> c_int
|
||||
}
|
||||
Some((parent_scope, _)) => visitor.scope_tree.record_var_scope(var_id, parent_scope),
|
||||
Some(parent_scope) => visitor.scope_tree.record_var_scope(var_id, parent_scope),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +119,7 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi
|
|||
// itself has returned.
|
||||
|
||||
visitor.enter_node_scope_with_dtor(blk.hir_id.local_id);
|
||||
visitor.cx.var_parent = visitor.cx.parent;
|
||||
visitor.cx.set_var_parent();
|
||||
|
||||
{
|
||||
// This block should be kept approximately in sync with
|
||||
|
|
@ -132,7 +138,7 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi
|
|||
local_id: blk.hir_id.local_id,
|
||||
data: ScopeData::Remainder(FirstStatementIndex::new(i)),
|
||||
});
|
||||
visitor.cx.var_parent = visitor.cx.parent;
|
||||
visitor.cx.set_var_parent();
|
||||
visitor.visit_stmt(statement);
|
||||
// We need to back out temporarily to the last enclosing scope
|
||||
// for the `else` block, so that even the temporaries receiving
|
||||
|
|
@ -157,7 +163,7 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi
|
|||
local_id: blk.hir_id.local_id,
|
||||
data: ScopeData::Remainder(FirstStatementIndex::new(i)),
|
||||
});
|
||||
visitor.cx.var_parent = visitor.cx.parent;
|
||||
visitor.cx.set_var_parent();
|
||||
visitor.visit_stmt(statement)
|
||||
}
|
||||
hir::StmtKind::Item(..) => {
|
||||
|
|
@ -207,7 +213,7 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir:
|
|||
visitor.terminating_scopes.insert(arm.hir_id.local_id);
|
||||
|
||||
visitor.enter_node_scope_with_dtor(arm.hir_id.local_id);
|
||||
visitor.cx.var_parent = visitor.cx.parent;
|
||||
visitor.cx.set_var_parent();
|
||||
|
||||
if let Some(expr) = arm.guard
|
||||
&& !has_let_expr(expr)
|
||||
|
|
@ -221,8 +227,6 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir:
|
|||
}
|
||||
|
||||
fn resolve_pat<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) {
|
||||
visitor.record_child_scope(Scope { local_id: pat.hir_id.local_id, data: ScopeData::Node });
|
||||
|
||||
// If this is a binding then record the lifetime of that binding.
|
||||
if let PatKind::Binding(..) = pat.kind {
|
||||
record_var_lifetime(visitor, pat.hir_id.local_id);
|
||||
|
|
@ -486,7 +490,7 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi
|
|||
ScopeData::IfThen
|
||||
};
|
||||
visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data });
|
||||
visitor.cx.var_parent = visitor.cx.parent;
|
||||
visitor.cx.set_var_parent();
|
||||
visitor.visit_expr(cond);
|
||||
visitor.visit_expr(then);
|
||||
visitor.cx = expr_cx;
|
||||
|
|
@ -501,7 +505,7 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi
|
|||
ScopeData::IfThen
|
||||
};
|
||||
visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data });
|
||||
visitor.cx.var_parent = visitor.cx.parent;
|
||||
visitor.cx.set_var_parent();
|
||||
visitor.visit_expr(cond);
|
||||
visitor.visit_expr(then);
|
||||
visitor.cx = expr_cx;
|
||||
|
|
@ -560,7 +564,7 @@ fn resolve_local<'tcx>(
|
|||
) {
|
||||
debug!("resolve_local(pat={:?}, init={:?})", pat, init);
|
||||
|
||||
let blk_scope = visitor.cx.var_parent.map(|(p, _)| p);
|
||||
let blk_scope = visitor.cx.var_parent;
|
||||
|
||||
// As an exception to the normal rules governing temporary
|
||||
// lifetimes, initializers in a let have a temporary lifetime
|
||||
|
|
@ -625,10 +629,7 @@ fn resolve_local<'tcx>(
|
|||
if is_binding_pat(pat) {
|
||||
visitor.scope_tree.record_rvalue_candidate(
|
||||
expr.hir_id,
|
||||
RvalueCandidateType::Pattern {
|
||||
target: expr.hir_id.local_id,
|
||||
lifetime: blk_scope,
|
||||
},
|
||||
RvalueCandidate { target: expr.hir_id.local_id, lifetime: blk_scope },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -733,10 +734,7 @@ fn resolve_local<'tcx>(
|
|||
record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id);
|
||||
visitor.scope_tree.record_rvalue_candidate(
|
||||
subexpr.hir_id,
|
||||
RvalueCandidateType::Borrow {
|
||||
target: subexpr.hir_id.local_id,
|
||||
lifetime: blk_id,
|
||||
},
|
||||
RvalueCandidate { target: subexpr.hir_id.local_id, lifetime: blk_id },
|
||||
);
|
||||
}
|
||||
hir::ExprKind::Struct(_, fields, _) => {
|
||||
|
|
@ -857,13 +855,12 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> {
|
|||
self.enter_body(body.value.hir_id, |this| {
|
||||
if this.tcx.hir_body_owner_kind(owner_id).is_fn_or_closure() {
|
||||
// The arguments and `self` are parented to the fn.
|
||||
this.cx.var_parent = this.cx.parent.take();
|
||||
this.cx.set_var_parent();
|
||||
for param in body.params {
|
||||
this.visit_pat(param.pat);
|
||||
}
|
||||
|
||||
// The body of the every fn is a root scope.
|
||||
this.cx.parent = this.cx.var_parent;
|
||||
this.visit_expr(body.value)
|
||||
} else {
|
||||
// Only functions have an outer terminating (drop) scope, while
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use hir::Node;
|
|||
use hir::def_id::DefId;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::middle::region::{RvalueCandidateType, Scope, ScopeTree};
|
||||
use rustc_middle::middle::region::{RvalueCandidate, Scope, ScopeTree};
|
||||
use rustc_middle::ty::RvalueScopes;
|
||||
use tracing::debug;
|
||||
|
||||
|
|
@ -55,15 +55,11 @@ fn record_rvalue_scope_rec(
|
|||
fn record_rvalue_scope(
|
||||
rvalue_scopes: &mut RvalueScopes,
|
||||
expr: &hir::Expr<'_>,
|
||||
candidate: &RvalueCandidateType,
|
||||
candidate: &RvalueCandidate,
|
||||
) {
|
||||
debug!("resolve_rvalue_scope(expr={expr:?}, candidate={candidate:?})");
|
||||
match candidate {
|
||||
RvalueCandidateType::Borrow { lifetime, .. }
|
||||
| RvalueCandidateType::Pattern { lifetime, .. } => {
|
||||
record_rvalue_scope_rec(rvalue_scopes, expr, *lifetime)
|
||||
} // FIXME(@dingxiangfei2009): handle the candidates in the function call arguments
|
||||
}
|
||||
record_rvalue_scope_rec(rvalue_scopes, expr, candidate.lifetime)
|
||||
// FIXME(@dingxiangfei2009): handle the candidates in the function call arguments
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_rvalue_scopes<'a, 'tcx>(
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ pub struct ScopeTree {
|
|||
/// and not the enclosing *statement*. Expressions that are not present in this
|
||||
/// table are not rvalue candidates. The set of rvalue candidates is computed
|
||||
/// during type check based on a traversal of the AST.
|
||||
pub rvalue_candidates: HirIdMap<RvalueCandidateType>,
|
||||
pub rvalue_candidates: HirIdMap<RvalueCandidate>,
|
||||
|
||||
/// Backwards incompatible scoping that will be introduced in future editions.
|
||||
/// This information is used later for linting to identify locals and
|
||||
|
|
@ -308,15 +308,14 @@ pub struct ScopeTree {
|
|||
pub yield_in_scope: UnordMap<Scope, Vec<YieldData>>,
|
||||
}
|
||||
|
||||
/// Identifies the reason that a given expression is an rvalue candidate
|
||||
/// (see the `rvalue_candidates` field for more information what rvalue
|
||||
/// candidates in general). In constants, the `lifetime` field is None
|
||||
/// to indicate that certain expressions escape into 'static and
|
||||
/// should have no local cleanup scope.
|
||||
/// See the `rvalue_candidates` field for more information on rvalue
|
||||
/// candidates in general.
|
||||
/// The `lifetime` field is None to indicate that certain expressions escape
|
||||
/// into 'static and should have no local cleanup scope.
|
||||
#[derive(Debug, Copy, Clone, HashStable)]
|
||||
pub enum RvalueCandidateType {
|
||||
Borrow { target: hir::ItemLocalId, lifetime: Option<Scope> },
|
||||
Pattern { target: hir::ItemLocalId, lifetime: Option<Scope> },
|
||||
pub struct RvalueCandidate {
|
||||
pub target: hir::ItemLocalId,
|
||||
pub lifetime: Option<Scope>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, HashStable)]
|
||||
|
|
@ -344,16 +343,12 @@ impl ScopeTree {
|
|||
self.var_map.insert(var, lifetime);
|
||||
}
|
||||
|
||||
pub fn record_rvalue_candidate(&mut self, var: HirId, candidate_type: RvalueCandidateType) {
|
||||
debug!("record_rvalue_candidate(var={var:?}, type={candidate_type:?})");
|
||||
match &candidate_type {
|
||||
RvalueCandidateType::Borrow { lifetime: Some(lifetime), .. }
|
||||
| RvalueCandidateType::Pattern { lifetime: Some(lifetime), .. } => {
|
||||
assert!(var.local_id != lifetime.local_id)
|
||||
}
|
||||
_ => {}
|
||||
pub fn record_rvalue_candidate(&mut self, var: HirId, candidate: RvalueCandidate) {
|
||||
debug!("record_rvalue_candidate(var={var:?}, candidate={candidate:?})");
|
||||
if let Some(lifetime) = &candidate.lifetime {
|
||||
assert!(var.local_id != lifetime.local_id)
|
||||
}
|
||||
self.rvalue_candidates.insert(var, candidate_type);
|
||||
self.rvalue_candidates.insert(var, candidate);
|
||||
}
|
||||
|
||||
/// Returns the narrowest scope that encloses `id`, if any.
|
||||
|
|
|
|||
|
|
@ -73,9 +73,7 @@ impl GenericParamDef {
|
|||
|
||||
pub fn is_anonymous_lifetime(&self) -> bool {
|
||||
match self.kind {
|
||||
GenericParamDefKind::Lifetime => {
|
||||
self.name == kw::UnderscoreLifetime || self.name == kw::Empty
|
||||
}
|
||||
GenericParamDefKind::Lifetime => self.name == kw::UnderscoreLifetime,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -457,7 +457,7 @@ impl EarlyParamRegion {
|
|||
/// Does this early bound region have a name? Early bound regions normally
|
||||
/// always have names except when using anonymous lifetimes (`'_`).
|
||||
pub fn has_name(&self) -> bool {
|
||||
self.name != kw::UnderscoreLifetime && self.name != kw::Empty
|
||||
self.name != kw::UnderscoreLifetime
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2591,11 +2591,9 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
|
|||
// to fit that into a short string. Hence the recommendation to use
|
||||
// `explain_region()` or `note_and_explain_region()`.
|
||||
match *region {
|
||||
ty::ReEarlyParam(ref data) => {
|
||||
if data.name != kw::Empty {
|
||||
p!(write("{}", data.name));
|
||||
return Ok(());
|
||||
}
|
||||
ty::ReEarlyParam(data) => {
|
||||
p!(write("{}", data.name));
|
||||
return Ok(());
|
||||
}
|
||||
ty::ReLateParam(ty::LateParamRegion { kind, .. }) => {
|
||||
if let Some(name) = kind.get_name() {
|
||||
|
|
@ -2834,7 +2832,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
|
|||
|
||||
(name, ty::BoundRegionKind::Named(CRATE_DEF_ID.to_def_id(), name))
|
||||
}
|
||||
ty::BoundRegionKind::Named(def_id, kw::UnderscoreLifetime | kw::Empty) => {
|
||||
ty::BoundRegionKind::Named(def_id, kw::UnderscoreLifetime) => {
|
||||
let name = next_name(self);
|
||||
|
||||
if let Some(lt_idx) = lifetime_idx {
|
||||
|
|
|
|||
|
|
@ -400,9 +400,7 @@ impl LateParamRegionKind {
|
|||
|
||||
pub fn is_named(&self) -> bool {
|
||||
match *self {
|
||||
LateParamRegionKind::Named(_, name) => {
|
||||
name != kw::UnderscoreLifetime && name != kw::Empty
|
||||
}
|
||||
LateParamRegionKind::Named(_, name) => name != kw::UnderscoreLifetime,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -475,7 +473,7 @@ impl core::fmt::Debug for BoundRegion {
|
|||
impl BoundRegionKind {
|
||||
pub fn is_named(&self) -> bool {
|
||||
match *self {
|
||||
BoundRegionKind::Named(_, name) => name != kw::UnderscoreLifetime && name != kw::Empty,
|
||||
BoundRegionKind::Named(_, name) => name != kw::UnderscoreLifetime,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ use rustc_span::{Span, Symbol, sym};
|
|||
use crate::{errors, parse_in};
|
||||
|
||||
pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
|
||||
if attr.is_doc_comment() || attr.has_name(sym::cfg_attr_trace) {
|
||||
if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -215,11 +216,7 @@ pub fn check_builtin_meta_item(
|
|||
template: AttributeTemplate,
|
||||
deny_unsafety: bool,
|
||||
) {
|
||||
// Some special attributes like `cfg` must be checked
|
||||
// before the generic check, so we skip them here.
|
||||
let should_skip = |name| name == sym::cfg;
|
||||
|
||||
if !should_skip(name) && !is_attr_template_compatible(&template, &meta.kind) {
|
||||
if !is_attr_template_compatible(&template, &meta.kind) {
|
||||
emit_malformed_attribute(psess, style, meta.span, name, template);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -272,6 +272,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
| sym::forbid
|
||||
| sym::cfg
|
||||
| sym::cfg_attr
|
||||
| sym::cfg_trace
|
||||
| sym::cfg_attr_trace
|
||||
// need to be fixed
|
||||
| sym::cfi_encoding // FIXME(cfi_encoding)
|
||||
|
|
@ -574,8 +575,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
// NOTE: when making changes to this list, check that `error_codes/E0736.md` remains accurate
|
||||
const ALLOW_LIST: &[rustc_span::Symbol] = &[
|
||||
// conditional compilation
|
||||
sym::cfg,
|
||||
sym::cfg_attr,
|
||||
sym::cfg_trace,
|
||||
sym::cfg_attr_trace,
|
||||
// testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
|
||||
sym::test,
|
||||
|
|
@ -2656,7 +2656,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
|
|||
// only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed
|
||||
// if we allow more attributes (e.g., tool attributes and `allow/deny/warn`)
|
||||
// in where clauses. After that, only `self.check_attributes` should be enough.
|
||||
const ATTRS_ALLOWED: &[Symbol] = &[sym::cfg, sym::cfg_attr, sym::cfg_attr_trace];
|
||||
const ATTRS_ALLOWED: &[Symbol] = &[sym::cfg_trace, sym::cfg_attr_trace];
|
||||
let spans = self
|
||||
.tcx
|
||||
.hir_attrs(where_predicate.hir_id)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ mod hcx;
|
|||
mod impls_syntax;
|
||||
|
||||
pub const IGNORED_ATTRIBUTES: &[Symbol] = &[
|
||||
sym::cfg,
|
||||
sym::cfg_trace, // FIXME should this really be ignored?
|
||||
sym::rustc_if_this_changed,
|
||||
sym::rustc_then_this_would_need,
|
||||
sym::rustc_dirty,
|
||||
|
|
|
|||
|
|
@ -1665,41 +1665,81 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
// the struct literal syntax at all, as that will cause a subsequent error.
|
||||
let fields = this.r.field_idents(def_id);
|
||||
let has_fields = fields.as_ref().is_some_and(|f| !f.is_empty());
|
||||
let (fields, applicability) = match fields {
|
||||
Some(fields) => {
|
||||
let fields = if let Some(old_fields) = old_fields {
|
||||
fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, new)| (new, old_fields.get(idx)))
|
||||
.map(|(new, old)| {
|
||||
if let Some(Some(old)) = old
|
||||
&& new.as_str() != old
|
||||
{
|
||||
format!("{new}: {old}")
|
||||
} else {
|
||||
new.to_string()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
} else {
|
||||
fields
|
||||
.iter()
|
||||
.map(|f| format!("{f}{tail}"))
|
||||
.collect::<Vec<String>>()
|
||||
};
|
||||
|
||||
(fields.join(", "), applicability)
|
||||
}
|
||||
None => ("/* fields */".to_string(), Applicability::HasPlaceholders),
|
||||
};
|
||||
let pad = if has_fields { " " } else { "" };
|
||||
err.span_suggestion(
|
||||
if let PathSource::Expr(Some(Expr {
|
||||
kind: ExprKind::Call(path, args),
|
||||
span,
|
||||
format!("use struct {descr} syntax instead"),
|
||||
format!("{path_str} {{{pad}{fields}{pad}}}"),
|
||||
applicability,
|
||||
);
|
||||
..
|
||||
})) = source
|
||||
&& !args.is_empty()
|
||||
&& let Some(fields) = &fields
|
||||
&& args.len() == fields.len()
|
||||
// Make sure we have same number of args as fields
|
||||
{
|
||||
let path_span = path.span;
|
||||
let mut parts = Vec::new();
|
||||
|
||||
// Start with the opening brace
|
||||
parts.push((
|
||||
path_span.shrink_to_hi().until(args[0].span),
|
||||
"{".to_owned(),
|
||||
));
|
||||
|
||||
for (field, arg) in fields.iter().zip(args.iter()) {
|
||||
// Add the field name before the argument
|
||||
parts.push((arg.span.shrink_to_lo(), format!("{}: ", field)));
|
||||
}
|
||||
|
||||
// Add the closing brace
|
||||
parts.push((
|
||||
args.last().unwrap().span.shrink_to_hi().until(span.shrink_to_hi()),
|
||||
"}".to_owned(),
|
||||
));
|
||||
|
||||
err.multipart_suggestion_verbose(
|
||||
format!("use struct {descr} syntax instead of calling"),
|
||||
parts,
|
||||
applicability,
|
||||
);
|
||||
} else {
|
||||
let (fields, applicability) = match fields {
|
||||
Some(fields) => {
|
||||
let fields = if let Some(old_fields) = old_fields {
|
||||
fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, new)| (new, old_fields.get(idx)))
|
||||
.map(|(new, old)| {
|
||||
if let Some(Some(old)) = old
|
||||
&& new.as_str() != old
|
||||
{
|
||||
format!("{new}: {old}")
|
||||
} else {
|
||||
new.to_string()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
} else {
|
||||
fields
|
||||
.iter()
|
||||
.map(|f| format!("{f}{tail}"))
|
||||
.collect::<Vec<String>>()
|
||||
};
|
||||
|
||||
(fields.join(", "), applicability)
|
||||
}
|
||||
None => {
|
||||
("/* fields */".to_string(), Applicability::HasPlaceholders)
|
||||
}
|
||||
};
|
||||
let pad = if has_fields { " " } else { "" };
|
||||
err.span_suggestion(
|
||||
span,
|
||||
format!("use struct {descr} syntax instead"),
|
||||
format!("{path_str} {{{pad}{fields}{pad}}}"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let PathSource::Expr(Some(Expr {
|
||||
kind: ExprKind::Call(path, args),
|
||||
|
|
|
|||
|
|
@ -623,6 +623,7 @@ symbols! {
|
|||
cfg_target_has_atomic_equal_alignment,
|
||||
cfg_target_thread_local,
|
||||
cfg_target_vendor,
|
||||
cfg_trace: "<cfg>", // must not be a valid identifier
|
||||
cfg_ub_checks,
|
||||
cfg_version,
|
||||
cfi,
|
||||
|
|
|
|||
|
|
@ -24,31 +24,11 @@ pub(crate) fn parse(config: &str) -> Config {
|
|||
|
||||
#[test]
|
||||
fn download_ci_llvm() {
|
||||
let config = parse("");
|
||||
let is_available = llvm::is_ci_llvm_available_for_target(&config, config.llvm_assertions);
|
||||
if is_available {
|
||||
assert!(config.llvm_from_ci);
|
||||
}
|
||||
|
||||
let config = Config::parse_inner(
|
||||
Flags::parse(&[
|
||||
"check".to_string(),
|
||||
"--config=/does/not/exist".to_string(),
|
||||
"--ci".to_string(),
|
||||
"false".to_string(),
|
||||
]),
|
||||
|&_| toml::from_str("llvm.download-ci-llvm = true"),
|
||||
);
|
||||
let is_available = llvm::is_ci_llvm_available_for_target(&config, config.llvm_assertions);
|
||||
if is_available {
|
||||
assert!(config.llvm_from_ci);
|
||||
}
|
||||
|
||||
let config = parse("llvm.download-ci-llvm = false");
|
||||
assert!(!config.llvm_from_ci);
|
||||
|
||||
let if_unchanged_config = parse("llvm.download-ci-llvm = \"if-unchanged\"");
|
||||
if if_unchanged_config.llvm_from_ci {
|
||||
if if_unchanged_config.llvm_from_ci && if_unchanged_config.is_running_on_ci {
|
||||
let has_changes = if_unchanged_config
|
||||
.last_modified_commit(LLVM_INVALIDATION_PATHS, "download-ci-llvm", true)
|
||||
.is_none();
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@ use std::fs::File;
|
|||
use std::io::BufWriter;
|
||||
use std::time::{Duration, Instant, SystemTime};
|
||||
|
||||
use build_helper::ci::CiEnv;
|
||||
use build_helper::metrics::{
|
||||
JsonInvocation, JsonInvocationSystemStats, JsonNode, JsonRoot, JsonStepSystemStats, Test,
|
||||
TestOutcome, TestSuite, TestSuiteMetadata,
|
||||
CiMetadata, JsonInvocation, JsonInvocationSystemStats, JsonNode, JsonRoot, JsonStepSystemStats,
|
||||
Test, TestOutcome, TestSuite, TestSuiteMetadata,
|
||||
};
|
||||
use sysinfo::{CpuRefreshKind, RefreshKind, System};
|
||||
|
||||
|
|
@ -217,7 +218,12 @@ impl BuildMetrics {
|
|||
children: steps.into_iter().map(|step| self.prepare_json_step(step)).collect(),
|
||||
});
|
||||
|
||||
let json = JsonRoot { format_version: CURRENT_FORMAT_VERSION, system_stats, invocations };
|
||||
let json = JsonRoot {
|
||||
format_version: CURRENT_FORMAT_VERSION,
|
||||
system_stats,
|
||||
invocations,
|
||||
ci_metadata: get_ci_metadata(CiEnv::current()),
|
||||
};
|
||||
|
||||
t!(std::fs::create_dir_all(dest.parent().unwrap()));
|
||||
let mut file = BufWriter::new(t!(File::create(&dest)));
|
||||
|
|
@ -245,6 +251,16 @@ impl BuildMetrics {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_ci_metadata(ci_env: CiEnv) -> Option<CiMetadata> {
|
||||
if ci_env != CiEnv::GitHubActions {
|
||||
return None;
|
||||
}
|
||||
let workflow_run_id =
|
||||
std::env::var("GITHUB_WORKFLOW_RUN_ID").ok().and_then(|id| id.parse::<u64>().ok())?;
|
||||
let repository = std::env::var("GITHUB_REPOSITORY").ok()?;
|
||||
Some(CiMetadata { workflow_run_id, repository })
|
||||
}
|
||||
|
||||
struct MetricsState {
|
||||
finished_steps: Vec<StepMetrics>,
|
||||
running_steps: Vec<StepMetrics>,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,19 @@ pub struct JsonRoot {
|
|||
pub format_version: usize,
|
||||
pub system_stats: JsonInvocationSystemStats,
|
||||
pub invocations: Vec<JsonInvocation>,
|
||||
#[serde(default)]
|
||||
pub ci_metadata: Option<CiMetadata>,
|
||||
}
|
||||
|
||||
/// Represents metadata about bootstrap's execution in CI.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CiMetadata {
|
||||
/// GitHub run ID of the workflow where bootstrap was executed.
|
||||
/// Note that the run ID will be shared amongst all jobs executed in that workflow.
|
||||
pub workflow_run_id: u64,
|
||||
/// Full name of a GitHub repository where bootstrap was executed in CI.
|
||||
/// e.g. `rust-lang-ci/rust`.
|
||||
pub repository: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use std::fmt::Debug;
|
||||
use std::time::Duration;
|
||||
|
||||
use build_helper::metrics::{
|
||||
BuildStep, JsonRoot, TestOutcome, TestSuite, TestSuiteMetadata, escape_step_name,
|
||||
|
|
@ -184,11 +185,70 @@ fn render_table(suites: BTreeMap<String, TestSuiteRecord>) -> String {
|
|||
}
|
||||
|
||||
/// Outputs a report of test differences between the `parent` and `current` commits.
|
||||
pub fn output_test_diffs(job_metrics: HashMap<JobName, JobMetrics>) {
|
||||
pub fn output_test_diffs(job_metrics: &HashMap<JobName, JobMetrics>) {
|
||||
let aggregated_test_diffs = aggregate_test_diffs(&job_metrics);
|
||||
report_test_diffs(aggregated_test_diffs);
|
||||
}
|
||||
|
||||
/// Prints the ten largest differences in bootstrap durations.
|
||||
pub fn output_largest_duration_changes(job_metrics: &HashMap<JobName, JobMetrics>) {
|
||||
struct Entry<'a> {
|
||||
job: &'a JobName,
|
||||
before: Duration,
|
||||
after: Duration,
|
||||
change: f64,
|
||||
}
|
||||
|
||||
let mut changes: Vec<Entry> = vec![];
|
||||
for (job, metrics) in job_metrics {
|
||||
if let Some(parent) = &metrics.parent {
|
||||
let duration_before = parent
|
||||
.invocations
|
||||
.iter()
|
||||
.map(|i| BuildStep::from_invocation(i).duration)
|
||||
.sum::<Duration>();
|
||||
let duration_after = metrics
|
||||
.current
|
||||
.invocations
|
||||
.iter()
|
||||
.map(|i| BuildStep::from_invocation(i).duration)
|
||||
.sum::<Duration>();
|
||||
let pct_change = duration_after.as_secs_f64() / duration_before.as_secs_f64();
|
||||
let pct_change = pct_change * 100.0;
|
||||
// Normalize around 100, to get + for regression and - for improvements
|
||||
let pct_change = pct_change - 100.0;
|
||||
changes.push(Entry {
|
||||
job,
|
||||
before: duration_before,
|
||||
after: duration_after,
|
||||
change: pct_change,
|
||||
});
|
||||
}
|
||||
}
|
||||
changes.sort_by(|e1, e2| e1.change.partial_cmp(&e2.change).unwrap().reverse());
|
||||
|
||||
println!("# Job duration changes");
|
||||
for (index, entry) in changes.into_iter().take(10).enumerate() {
|
||||
println!(
|
||||
"{}. `{}`: {:.1}s -> {:.1}s ({:.1}%)",
|
||||
index + 1,
|
||||
entry.job,
|
||||
entry.before.as_secs_f64(),
|
||||
entry.after.as_secs_f64(),
|
||||
entry.change
|
||||
);
|
||||
}
|
||||
|
||||
println!();
|
||||
output_details("How to interpret the job duration changes?", || {
|
||||
println!(
|
||||
r#"Job durations can vary a lot, based on the actual runner instance
|
||||
that executed the job, system noise, invalidated caches, etc. The table above is provided
|
||||
mostly for t-infra members, for simpler debugging of potential CI slow-downs."#
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct TestSuiteRecord {
|
||||
passed: u64,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use clap::Parser;
|
|||
use jobs::JobDatabase;
|
||||
use serde_yaml::Value;
|
||||
|
||||
use crate::analysis::output_test_diffs;
|
||||
use crate::analysis::{output_largest_duration_changes, output_test_diffs};
|
||||
use crate::cpu_usage::load_cpu_usage;
|
||||
use crate::datadog::upload_datadog_metric;
|
||||
use crate::jobs::RunType;
|
||||
|
|
@ -160,7 +160,7 @@ fn postprocess_metrics(
|
|||
job_name,
|
||||
JobMetrics { parent: Some(parent_metrics), current: metrics },
|
||||
)]);
|
||||
output_test_diffs(job_metrics);
|
||||
output_test_diffs(&job_metrics);
|
||||
return Ok(());
|
||||
}
|
||||
Err(error) => {
|
||||
|
|
@ -180,7 +180,8 @@ fn post_merge_report(db: JobDatabase, current: String, parent: String) -> anyhow
|
|||
let metrics = download_auto_job_metrics(&db, &parent, ¤t)?;
|
||||
|
||||
println!("\nComparing {parent} (parent) -> {current} (this PR)\n");
|
||||
output_test_diffs(metrics);
|
||||
output_test_diffs(&metrics);
|
||||
output_largest_duration_changes(&metrics);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Context;
|
||||
use build_helper::metrics::{JsonNode, JsonRoot, TestSuite};
|
||||
|
|
@ -74,6 +74,17 @@ Maybe it was newly added?"#,
|
|||
}
|
||||
|
||||
pub fn download_job_metrics(job_name: &str, sha: &str) -> anyhow::Result<JsonRoot> {
|
||||
// Best effort cache to speed-up local re-executions of citool
|
||||
let cache_path = PathBuf::from(".citool-cache").join(sha).join(format!("{job_name}.json"));
|
||||
if cache_path.is_file() {
|
||||
if let Ok(metrics) = std::fs::read_to_string(&cache_path)
|
||||
.map_err(|err| err.into())
|
||||
.and_then(|data| anyhow::Ok::<JsonRoot>(serde_json::from_str::<JsonRoot>(&data)?))
|
||||
{
|
||||
return Ok(metrics);
|
||||
}
|
||||
}
|
||||
|
||||
let url = get_metrics_url(job_name, sha);
|
||||
let mut response = ureq::get(&url).call()?;
|
||||
if !response.status().is_success() {
|
||||
|
|
@ -87,6 +98,13 @@ pub fn download_job_metrics(job_name: &str, sha: &str) -> anyhow::Result<JsonRoo
|
|||
.body_mut()
|
||||
.read_json()
|
||||
.with_context(|| anyhow::anyhow!("cannot deserialize metrics from {url}"))?;
|
||||
|
||||
if let Ok(_) = std::fs::create_dir_all(cache_path.parent().unwrap()) {
|
||||
if let Ok(data) = serde_json::to_string(&data) {
|
||||
let _ = std::fs::write(cache_path, data);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ where
|
|||
println!(
|
||||
r"<details>
|
||||
<summary>{summary}</summary>
|
||||
|
||||
"
|
||||
);
|
||||
func();
|
||||
|
|
|
|||
|
|
@ -355,6 +355,8 @@ docker \
|
|||
--env GITHUB_ACTIONS \
|
||||
--env GITHUB_REF \
|
||||
--env GITHUB_STEP_SUMMARY="/checkout/obj/${SUMMARY_FILE}" \
|
||||
--env GITHUB_WORKFLOW_RUN_ID \
|
||||
--env GITHUB_REPOSITORY \
|
||||
--env RUST_BACKTRACE \
|
||||
--env TOOLSTATE_REPO_ACCESS_TOKEN \
|
||||
--env TOOLSTATE_REPO \
|
||||
|
|
|
|||
|
|
@ -1943,14 +1943,11 @@ fn clean_trait_object_lifetime_bound<'tcx>(
|
|||
// latter contrary to `clean_middle_region`.
|
||||
match *region {
|
||||
ty::ReStatic => Some(Lifetime::statik()),
|
||||
ty::ReEarlyParam(region) if region.name != kw::Empty => Some(Lifetime(region.name)),
|
||||
ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(_, name), .. })
|
||||
if name != kw::Empty =>
|
||||
{
|
||||
ty::ReEarlyParam(region) => Some(Lifetime(region.name)),
|
||||
ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(_, name), .. }) => {
|
||||
Some(Lifetime(name))
|
||||
}
|
||||
ty::ReEarlyParam(_)
|
||||
| ty::ReBound(..)
|
||||
ty::ReBound(..)
|
||||
| ty::ReLateParam(_)
|
||||
| ty::ReVar(_)
|
||||
| ty::RePlaceholder(_)
|
||||
|
|
@ -2773,7 +2770,7 @@ fn add_without_unwanted_attributes<'hir>(
|
|||
if ident == sym::doc {
|
||||
filter_doc_attr(&mut normal.args, is_inline);
|
||||
attrs.push((Cow::Owned(attr), import_parent));
|
||||
} else if is_inline || ident != sym::cfg {
|
||||
} else if is_inline || ident != sym::cfg_trace {
|
||||
// If it's not a `cfg()` attribute, we keep it.
|
||||
attrs.push((Cow::Owned(attr), import_parent));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1068,7 +1068,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
|
|||
// `doc(cfg())` overrides `cfg()`).
|
||||
attrs
|
||||
.clone()
|
||||
.filter(|attr| attr.has_name(sym::cfg))
|
||||
.filter(|attr| attr.has_name(sym::cfg_trace))
|
||||
.filter_map(|attr| single(attr.meta_item_list()?))
|
||||
.filter_map(|attr| Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten())
|
||||
.fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ fn check_duplicated_attr(
|
|||
let Some(ident) = attr.ident() else { return };
|
||||
let name = ident.name;
|
||||
if name == sym::doc
|
||||
|| name == sym::cfg_attr
|
||||
|| name == sym::cfg_attr_trace
|
||||
|| name == sym::rustc_on_unimplemented
|
||||
|| name == sym::reason {
|
||||
|
|
@ -47,7 +46,7 @@ fn check_duplicated_attr(
|
|||
return;
|
||||
}
|
||||
if let Some(direct_parent) = parent.last()
|
||||
&& ["cfg", "cfg_attr"].contains(&direct_parent.as_str())
|
||||
&& direct_parent == sym::cfg_trace.as_str()
|
||||
&& [sym::all, sym::not, sym::any].contains(&name)
|
||||
{
|
||||
// FIXME: We don't correctly check `cfg`s for now, so if it's more complex than just a one
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ declare_lint_pass!(CfgNotTest => [CFG_NOT_TEST]);
|
|||
|
||||
impl EarlyLintPass for CfgNotTest {
|
||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) {
|
||||
if attr.has_name(rustc_span::sym::cfg) && contains_not_test(attr.meta_item_list().as_deref(), false) {
|
||||
if attr.has_name(rustc_span::sym::cfg_trace) && contains_not_test(attr.meta_item_list().as_deref(), false) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CFG_NOT_TEST,
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_
|
|||
fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool {
|
||||
cx.tcx
|
||||
.hir_parent_id_iter(id)
|
||||
.any(|id| cx.tcx.hir_attrs(id).iter().any(|attr| attr.has_name(sym::cfg)))
|
||||
.any(|id| cx.tcx.hir_attrs(id).iter().any(|attr| attr.has_name(sym::cfg_trace)))
|
||||
}
|
||||
|
||||
/// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization
|
||||
|
|
|
|||
|
|
@ -2629,7 +2629,7 @@ pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>
|
|||
pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
|
||||
if let Res::Def(_, def_id) = path.res {
|
||||
return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
|
||||
return cx.tcx.has_attr(def_id, sym::cfg_trace) || cx.tcx.has_attr(def_id, sym::cfg_attr);
|
||||
}
|
||||
}
|
||||
false
|
||||
|
|
@ -2699,7 +2699,7 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
|
|||
/// use [`is_in_cfg_test`]
|
||||
pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
|
||||
tcx.hir_attrs(id).iter().any(|attr| {
|
||||
if attr.has_name(sym::cfg)
|
||||
if attr.has_name(sym::cfg_trace)
|
||||
&& let Some(items) = attr.meta_item_list()
|
||||
&& let [item] = &*items
|
||||
&& item.has_name(sym::test)
|
||||
|
|
@ -2723,11 +2723,11 @@ pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
|
|||
|
||||
/// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied.
|
||||
pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
tcx.has_attr(def_id, sym::cfg)
|
||||
tcx.has_attr(def_id, sym::cfg_trace)
|
||||
|| tcx
|
||||
.hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
|
||||
.flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
|
||||
.any(|attr| attr.has_name(sym::cfg))
|
||||
.any(|attr| attr.has_name(sym::cfg_trace))
|
||||
}
|
||||
|
||||
/// Walks up the HIR tree from the given expression in an attempt to find where the value is
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ pub mod util;
|
|||
use core::panic;
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::OsString;
|
||||
use std::fmt::Write;
|
||||
use std::io::{self, ErrorKind};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
|
|
@ -570,18 +571,22 @@ pub fn run_tests(config: Arc<Config>) {
|
|||
// easy to miss which tests failed, and as such fail to reproduce
|
||||
// the failure locally.
|
||||
|
||||
println!(
|
||||
"Some tests failed in compiletest suite={}{} mode={} host={} target={}",
|
||||
config.suite,
|
||||
config
|
||||
.compare_mode
|
||||
.as_ref()
|
||||
.map(|c| format!(" compare_mode={:?}", c))
|
||||
.unwrap_or_default(),
|
||||
config.mode,
|
||||
config.host,
|
||||
config.target
|
||||
);
|
||||
let mut msg = String::from("Some tests failed in compiletest");
|
||||
write!(msg, " suite={}", config.suite).unwrap();
|
||||
|
||||
if let Some(compare_mode) = config.compare_mode.as_ref() {
|
||||
write!(msg, " compare_mode={}", compare_mode).unwrap();
|
||||
}
|
||||
|
||||
if let Some(pass_mode) = config.force_pass_mode.as_ref() {
|
||||
write!(msg, " pass_mode={}", pass_mode).unwrap();
|
||||
}
|
||||
|
||||
write!(msg, " mode={}", config.mode).unwrap();
|
||||
write!(msg, " host={}", config.host).unwrap();
|
||||
write!(msg, " target={}", config.target).unwrap();
|
||||
|
||||
println!("{msg}");
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ extern crate std;
|
|||
//@ pp-exact:tests-are-sorted.pp
|
||||
|
||||
extern crate test;
|
||||
#[cfg(test)]
|
||||
#[rustc_test_marker = "m_test"]
|
||||
#[doc(hidden)]
|
||||
pub const m_test: test::TestDescAndFn =
|
||||
|
|
@ -35,7 +34,6 @@ pub const m_test: test::TestDescAndFn =
|
|||
fn m_test() {}
|
||||
|
||||
extern crate test;
|
||||
#[cfg(test)]
|
||||
#[rustc_test_marker = "z_test"]
|
||||
#[doc(hidden)]
|
||||
pub const z_test: test::TestDescAndFn =
|
||||
|
|
@ -61,7 +59,6 @@ pub const z_test: test::TestDescAndFn =
|
|||
fn z_test() {}
|
||||
|
||||
extern crate test;
|
||||
#[cfg(test)]
|
||||
#[rustc_test_marker = "a_test"]
|
||||
#[doc(hidden)]
|
||||
pub const a_test: test::TestDescAndFn =
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ macro_rules! generate_s10 {
|
|||
($expr: expr) => {
|
||||
#[cfg(feature = $expr)]
|
||||
//~^ ERROR expected unsuffixed literal, found expression `concat!("nonexistent")`
|
||||
//~| ERROR expected unsuffixed literal, found expression `concat!("nonexistent")`
|
||||
struct S10;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,19 +65,7 @@ LL | generate_s10!(concat!("nonexistent"));
|
|||
|
|
||||
= note: this error originates in the macro `generate_s10` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: expected unsuffixed literal, found expression `concat!("nonexistent")`
|
||||
--> $DIR/cfg-attr-syntax-validation.rs:30:25
|
||||
|
|
||||
LL | #[cfg(feature = $expr)]
|
||||
| ^^^^^
|
||||
...
|
||||
LL | generate_s10!(concat!("nonexistent"));
|
||||
| ------------------------------------- in this macro invocation
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
= note: this error originates in the macro `generate_s10` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0537, E0565.
|
||||
For more information about an error, try `rustc --explain E0537`.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
// This was triggering an assertion failure in `NodeRange::new`.
|
||||
|
||||
//@ check-pass
|
||||
|
||||
#![feature(cfg_eval)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
|
||||
fn f() -> u32 {
|
||||
#[cfg_eval] #[cfg(not(FALSE))] 0
|
||||
//~^ ERROR removing an expression is not supported in this position
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
error: removing an expression is not supported in this position
|
||||
--> $DIR/invalid-node-range-issue-129166.rs:7:17
|
||||
|
|
||||
LL | #[cfg_eval] #[cfg(not(FALSE))] 0
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -2,7 +2,6 @@ macro_rules! mac {
|
|||
($attr_item: meta) => {
|
||||
#[cfg($attr_item)]
|
||||
//~^ ERROR expected unsuffixed literal, found `meta` metavariable
|
||||
//~| ERROR expected unsuffixed literal, found `meta` metavariable
|
||||
struct S;
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +10,6 @@ mac!(an(arbitrary token stream));
|
|||
|
||||
#[cfg(feature = -1)]
|
||||
//~^ ERROR expected unsuffixed literal, found `-`
|
||||
//~| ERROR expected unsuffixed literal, found `-`
|
||||
fn handler() {}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error: expected unsuffixed literal, found `-`
|
||||
--> $DIR/attr-bad-meta-4.rs:12:17
|
||||
--> $DIR/attr-bad-meta-4.rs:11:17
|
||||
|
|
||||
LL | #[cfg(feature = -1)]
|
||||
| ^
|
||||
|
|
@ -15,25 +15,5 @@ LL | mac!(an(arbitrary token stream));
|
|||
|
|
||||
= note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: expected unsuffixed literal, found `meta` metavariable
|
||||
--> $DIR/attr-bad-meta-4.rs:3:15
|
||||
|
|
||||
LL | #[cfg($attr_item)]
|
||||
| ^^^^^^^^^^
|
||||
...
|
||||
LL | mac!(an(arbitrary token stream));
|
||||
| -------------------------------- in this macro invocation
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
= note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: expected unsuffixed literal, found `-`
|
||||
--> $DIR/attr-bad-meta-4.rs:12:17
|
||||
|
|
||||
LL | #[cfg(feature = -1)]
|
||||
| ^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
//@ check-pass
|
||||
//@ proc-macro: test-macros.rs
|
||||
|
||||
#![feature(cfg_boolean_literals)]
|
||||
#![feature(cfg_eval)]
|
||||
|
||||
#[macro_use]
|
||||
|
|
@ -10,8 +11,13 @@ extern crate test_macros;
|
|||
|
||||
#[cfg_eval]
|
||||
#[test_macros::print_attr]
|
||||
#[cfg_attr(FALSE, test_macros::print_attr)]
|
||||
#[cfg_attr(all(), test_macros::print_attr)]
|
||||
#[cfg_attr(false, test_macros::print_attr)]
|
||||
#[cfg_attr(true, test_macros::print_attr)]
|
||||
struct S;
|
||||
|
||||
#[cfg_eval]
|
||||
#[test_macros::print_attr]
|
||||
#[cfg(true)]
|
||||
struct Z;
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -4,59 +4,75 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
|
|||
Punct {
|
||||
ch: '#',
|
||||
spacing: Alone,
|
||||
span: #0 bytes(271..272),
|
||||
span: #0 bytes(305..306),
|
||||
},
|
||||
Group {
|
||||
delimiter: Bracket,
|
||||
stream: TokenStream [
|
||||
Ident {
|
||||
ident: "test_macros",
|
||||
span: #0 bytes(289..300),
|
||||
span: #0 bytes(322..333),
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Joint,
|
||||
span: #0 bytes(300..301),
|
||||
span: #0 bytes(333..334),
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Alone,
|
||||
span: #0 bytes(301..302),
|
||||
span: #0 bytes(334..335),
|
||||
},
|
||||
Ident {
|
||||
ident: "print_attr",
|
||||
span: #0 bytes(302..312),
|
||||
span: #0 bytes(335..345),
|
||||
},
|
||||
],
|
||||
span: #0 bytes(272..314),
|
||||
span: #0 bytes(306..347),
|
||||
},
|
||||
Ident {
|
||||
ident: "struct",
|
||||
span: #0 bytes(315..321),
|
||||
span: #0 bytes(348..354),
|
||||
},
|
||||
Ident {
|
||||
ident: "S",
|
||||
span: #0 bytes(322..323),
|
||||
span: #0 bytes(355..356),
|
||||
},
|
||||
Punct {
|
||||
ch: ';',
|
||||
spacing: Alone,
|
||||
span: #0 bytes(323..324),
|
||||
span: #0 bytes(356..357),
|
||||
},
|
||||
]
|
||||
PRINT-ATTR INPUT (DISPLAY): struct S;
|
||||
PRINT-ATTR INPUT (DEBUG): TokenStream [
|
||||
Ident {
|
||||
ident: "struct",
|
||||
span: #0 bytes(315..321),
|
||||
span: #0 bytes(348..354),
|
||||
},
|
||||
Ident {
|
||||
ident: "S",
|
||||
span: #0 bytes(322..323),
|
||||
span: #0 bytes(355..356),
|
||||
},
|
||||
Punct {
|
||||
ch: ';',
|
||||
spacing: Alone,
|
||||
span: #0 bytes(323..324),
|
||||
span: #0 bytes(356..357),
|
||||
},
|
||||
]
|
||||
PRINT-ATTR INPUT (DISPLAY): struct Z;
|
||||
PRINT-ATTR INPUT (DEBUG): TokenStream [
|
||||
Ident {
|
||||
ident: "struct",
|
||||
span: #0 bytes(411..417),
|
||||
},
|
||||
Ident {
|
||||
ident: "Z",
|
||||
span: #0 bytes(418..419),
|
||||
},
|
||||
Punct {
|
||||
ch: ';',
|
||||
spacing: Alone,
|
||||
span: #0 bytes(419..420),
|
||||
},
|
||||
]
|
||||
|
|
|
|||
25
tests/ui/structs/struct-construct-with-call-issue-138931.rs
Normal file
25
tests/ui/structs/struct-construct-with-call-issue-138931.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
struct PersonOnlyName {
|
||||
name: String
|
||||
}
|
||||
|
||||
struct PersonWithAge {
|
||||
name: String,
|
||||
age: u8,
|
||||
height: u8,
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn main() {
|
||||
let wilfred = PersonOnlyName("Name1".to_owned());
|
||||
//~^ ERROR expected function, tuple struct or tuple variant, found struct `PersonOnlyName` [E0423]
|
||||
|
||||
let bill = PersonWithAge( //~ ERROR expected function, tuple struct or tuple variant, found struct `PersonWithAge` [E0423]
|
||||
"Name2".to_owned(),
|
||||
20,
|
||||
180,
|
||||
);
|
||||
|
||||
let person = PersonWithAge("Name3".to_owned());
|
||||
//~^ ERROR expected function, tuple struct or tuple variant, found struct `PersonWithAge` [E0423]
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonOnlyName`
|
||||
--> $DIR/struct-construct-with-call-issue-138931.rs:14:19
|
||||
|
|
||||
LL | / struct PersonOnlyName {
|
||||
LL | | name: String
|
||||
LL | | }
|
||||
| |_- `PersonOnlyName` defined here
|
||||
...
|
||||
LL | let wilfred = PersonOnlyName("Name1".to_owned());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: use struct literal syntax instead of calling
|
||||
|
|
||||
LL - let wilfred = PersonOnlyName("Name1".to_owned());
|
||||
LL + let wilfred = PersonOnlyName{name: "Name1".to_owned()};
|
||||
|
|
||||
|
||||
error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonWithAge`
|
||||
--> $DIR/struct-construct-with-call-issue-138931.rs:17:16
|
||||
|
|
||||
LL | / struct PersonWithAge {
|
||||
LL | | name: String,
|
||||
LL | | age: u8,
|
||||
LL | | height: u8,
|
||||
LL | | }
|
||||
| |_- `PersonWithAge` defined here
|
||||
...
|
||||
LL | let bill = PersonWithAge(
|
||||
| ________________^
|
||||
LL | | "Name2".to_owned(),
|
||||
LL | | 20,
|
||||
LL | | 180,
|
||||
LL | | );
|
||||
| |_____^
|
||||
|
|
||||
help: use struct literal syntax instead of calling
|
||||
|
|
||||
LL ~ let bill = PersonWithAge{name: "Name2".to_owned(),
|
||||
LL ~ age: 20,
|
||||
LL ~ height: 180};
|
||||
|
|
||||
|
||||
error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonWithAge`
|
||||
--> $DIR/struct-construct-with-call-issue-138931.rs:23:18
|
||||
|
|
||||
LL | / struct PersonWithAge {
|
||||
LL | | name: String,
|
||||
LL | | age: u8,
|
||||
LL | | height: u8,
|
||||
LL | | }
|
||||
| |_- `PersonWithAge` defined here
|
||||
...
|
||||
LL | let person = PersonWithAge("Name3".to_owned());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `PersonWithAge { name: val, age: val, height: val }`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0423`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue