1
Fork 0

Auto merge of #99346 - matthiaskrgr:rollup-p4dl1qt, r=matthiaskrgr

Rollup of 10 pull requests

Successful merges:

 - #98582 (Allow destructuring opaque types in their defining scopes)
 - #99213 (migrate some of `rustc_passes::check_attr`'s diagnostics and derive improvements)
 - #99258 (Provide structured suggestion for dropped temp value)
 - #99259 (interpret/visitor: support visiting with a PlaceTy)
 - #99287 ([rustdoc-json] JSON no longer inlines)
 - #99290 (Revert "Highlight conflicting param-env candidates")
 - #99316 (docs: add missing word)
 - #99317 (Borrow Vec<T, A> as [T])
 - #99323 (Fix flakyness of GUI tests)
 - #99342 (Avoid some `Symbol` to `String` conversions)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2022-07-16 20:31:42 +00:00
commit d5e7f4782e
165 changed files with 2270 additions and 1215 deletions

View file

@ -4239,6 +4239,7 @@ dependencies = [
"rustc_hir", "rustc_hir",
"rustc_index", "rustc_index",
"rustc_lexer", "rustc_lexer",
"rustc_macros",
"rustc_middle", "rustc_middle",
"rustc_serialize", "rustc_serialize",
"rustc_session", "rustc_session",

View file

@ -7,7 +7,7 @@ use rustc_errors::{
}; };
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::ObligationCause; use rustc_infer::traits::ObligationCause;
@ -23,7 +23,7 @@ use rustc_middle::ty::{
use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
use rustc_span::hygiene::DesugaringKind; use rustc_span::hygiene::DesugaringKind;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::{BytePos, Span}; use rustc_span::{BytePos, Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::TraitEngineExt as _; use rustc_trait_selection::traits::TraitEngineExt as _;
@ -1227,8 +1227,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
from_closure: false, from_closure: false,
region_name: region_name:
RegionName { RegionName {
source: source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
.. ..
}, },
span, span,
@ -1500,7 +1499,70 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
| BorrowExplanation::UsedLaterInLoop(..) | BorrowExplanation::UsedLaterInLoop(..)
| BorrowExplanation::UsedLaterWhenDropped { .. } => { | BorrowExplanation::UsedLaterWhenDropped { .. } => {
// Only give this note and suggestion if it could be relevant. // Only give this note and suggestion if it could be relevant.
err.note("consider using a `let` binding to create a longer lived value"); let sm = self.infcx.tcx.sess.source_map();
let mut suggested = false;
let msg = "consider using a `let` binding to create a longer lived value";
/// We check that there's a single level of block nesting to ensure always correct
/// suggestions. If we don't, then we only provide a free-form message to avoid
/// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`.
/// We could expand the analysis to suggest hoising all of the relevant parts of
/// the users' code to make the code compile, but that could be too much.
struct NestedStatementVisitor {
span: Span,
current: usize,
found: usize,
}
impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
fn visit_block(&mut self, block: &hir::Block<'tcx>) {
self.current += 1;
walk_block(self, block);
self.current -= 1;
}
fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
if self.span == expr.span {
self.found = self.current;
}
walk_expr(self, expr);
}
}
let source_info = self.body.source_info(location);
if let Some(scope) = self.body.source_scopes.get(source_info.scope)
&& let ClearCrossCrate::Set(scope_data) = &scope.local_data
&& let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root)
&& let Some(id) = node.body_id()
&& let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
{
for stmt in block.stmts {
let mut visitor = NestedStatementVisitor {
span: proper_span,
current: 0,
found: 0,
};
visitor.visit_stmt(stmt);
if visitor.found == 0
&& stmt.span.contains(proper_span)
&& let Some(p) = sm.span_to_margin(stmt.span)
&& let Ok(s) = sm.span_to_snippet(proper_span)
{
let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
err.multipart_suggestion_verbose(
msg,
vec![
(stmt.span.shrink_to_lo(), addition),
(proper_span, "binding".to_string()),
],
Applicability::MaybeIncorrect,
);
suggested = true;
break;
}
}
}
if !suggested {
err.note(msg);
}
} }
_ => {} _ => {}
} }
@ -1699,7 +1761,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
borrow_span: Span, borrow_span: Span,
name: &Option<String>, name: &Option<String>,
upvar_span: Span, upvar_span: Span,
upvar_name: &str, upvar_name: Symbol,
escape_span: Span, escape_span: Span,
) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
let tcx = self.infcx.tcx; let tcx = self.infcx.tcx;
@ -2093,7 +2155,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
} }
StorageDeadOrDrop::Destructor(_) => kind, StorageDeadOrDrop::Destructor(_) => kind,
}, },
ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Field(..)
| ProjectionElem::Downcast(..) => {
match place_ty.ty.kind() { match place_ty.ty.kind() {
ty::Adt(def, _) if def.has_dtor(tcx) => { ty::Adt(def, _) if def.has_dtor(tcx) => {
// Report the outermost adt with a destructor // Report the outermost adt with a destructor

View file

@ -12,7 +12,7 @@ use rustc_middle::mir::{
}; };
use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::adjustment::PointerCast;
use rustc_middle::ty::{self, RegionVid, TyCtxt}; use rustc_middle::ty::{self, RegionVid, TyCtxt};
use rustc_span::symbol::Symbol; use rustc_span::symbol::{kw, Symbol};
use rustc_span::{sym, DesugaringKind, Span}; use rustc_span::{sym, DesugaringKind, Span};
use crate::region_infer::BlameConstraint; use crate::region_infer::BlameConstraint;
@ -282,7 +282,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
) { ) {
if let ConstraintCategory::OpaqueType = category { if let ConstraintCategory::OpaqueType = category {
let suggestable_name = let suggestable_name =
if region_name.was_named() { region_name.to_string() } else { "'_".to_string() }; if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime };
let msg = format!( let msg = format!(
"you can add a bound to the {}to make it last less than `'static` and match `{}`", "you can add a bound to the {}to make it last less than `'static` and match `{}`",

View file

@ -226,6 +226,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
} }
ProjectionElem::Downcast(..) if including_downcast.0 => return None, ProjectionElem::Downcast(..) if including_downcast.0 => return None,
ProjectionElem::Downcast(..) => (), ProjectionElem::Downcast(..) => (),
ProjectionElem::OpaqueCast(..) => (),
ProjectionElem::Field(field, _ty) => { ProjectionElem::Field(field, _ty) => {
// FIXME(project-rfc_2229#36): print capture precisely here. // FIXME(project-rfc_2229#36): print capture precisely here.
if let Some(field) = self.is_upvar_field_projection(PlaceRef { if let Some(field) = self.is_upvar_field_projection(PlaceRef {
@ -286,6 +287,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx) PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
} }
ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx), ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(*ty),
ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type), ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
}, },
}; };

View file

@ -169,6 +169,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
.., ..,
ProjectionElem::Index(_) ProjectionElem::Index(_)
| ProjectionElem::ConstantIndex { .. } | ProjectionElem::ConstantIndex { .. }
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Subslice { .. } | ProjectionElem::Subslice { .. }
| ProjectionElem::Downcast(..), | ProjectionElem::Downcast(..),
], ],

View file

@ -19,8 +19,7 @@ use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::Region; use rustc_middle::ty::Region;
use rustc_middle::ty::TypeVisitor; use rustc_middle::ty::TypeVisitor;
use rustc_middle::ty::{self, RegionVid, Ty}; use rustc_middle::ty::{self, RegionVid, Ty};
use rustc_span::symbol::sym; use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::symbol::Ident;
use rustc_span::Span; use rustc_span::Span;
use crate::borrowck_errors; use crate::borrowck_errors;
@ -758,7 +757,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
return; return;
}; };
let lifetime = if f.has_name() { fr_name.to_string() } else { "'_".to_string() }; let lifetime = if f.has_name() { fr_name.name } else { kw::UnderscoreLifetime };
let arg = match param.param.pat.simple_ident() { let arg = match param.param.pat.simple_ident() {
Some(simple_ident) => format!("argument `{}`", simple_ident), Some(simple_ident) => format!("argument `{}`", simple_ident),
@ -770,7 +769,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
self.infcx.tcx, self.infcx.tcx,
diag, diag,
fn_returns, fn_returns,
lifetime, lifetime.to_string(),
Some(arg), Some(arg),
captures, captures,
Some((param.param_ty_span, param.param_ty.to_string())), Some((param.param_ty_span, param.param_ty.to_string())),

View file

@ -34,13 +34,13 @@ pub(crate) enum RegionNameSource {
/// The `'static` region. /// The `'static` region.
Static, Static,
/// The free region corresponding to the environment of a closure. /// The free region corresponding to the environment of a closure.
SynthesizedFreeEnvRegion(Span, String), SynthesizedFreeEnvRegion(Span, &'static str),
/// The region corresponding to an argument. /// The region corresponding to an argument.
AnonRegionFromArgument(RegionNameHighlight), AnonRegionFromArgument(RegionNameHighlight),
/// The region corresponding to a closure upvar. /// The region corresponding to a closure upvar.
AnonRegionFromUpvar(Span, String), AnonRegionFromUpvar(Span, Symbol),
/// The region corresponding to the return type of a closure. /// The region corresponding to the return type of a closure.
AnonRegionFromOutput(RegionNameHighlight, String), AnonRegionFromOutput(RegionNameHighlight, &'static str),
/// The region from a type yielded by a generator. /// The region from a type yielded by a generator.
AnonRegionFromYieldTy(Span, String), AnonRegionFromYieldTy(Span, String),
/// An anonymous region from an async fn. /// An anonymous region from an async fn.
@ -110,7 +110,7 @@ impl RegionName {
} }
RegionNameSource::SynthesizedFreeEnvRegion(span, note) => { RegionNameSource::SynthesizedFreeEnvRegion(span, note) => {
diag.span_label(*span, format!("lifetime `{self}` represents this closure's body")); diag.span_label(*span, format!("lifetime `{self}` represents this closure's body"));
diag.note(note); diag.note(*note);
} }
RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy( RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy(
span, span,
@ -350,10 +350,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
Some(RegionName { Some(RegionName {
name: region_name, name: region_name,
source: RegionNameSource::SynthesizedFreeEnvRegion( source: RegionNameSource::SynthesizedFreeEnvRegion(fn_decl_span, note),
fn_decl_span,
note.to_string(),
),
}) })
} }
@ -678,7 +675,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
Some(RegionName { Some(RegionName {
name: region_name, name: region_name,
source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name.to_string()), source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
}) })
} }
@ -756,7 +753,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
Some(RegionName { Some(RegionName {
name: self.synthesize_region_name(), name: self.synthesize_region_name(),
source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description.to_string()), source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description),
}) })
} }

View file

@ -1788,6 +1788,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
for (place_base, elem) in place.iter_projections().rev() { for (place_base, elem) in place.iter_projections().rev() {
match elem { match elem {
ProjectionElem::Index(_/*operand*/) | ProjectionElem::Index(_/*operand*/) |
ProjectionElem::OpaqueCast(_) |
ProjectionElem::ConstantIndex { .. } | ProjectionElem::ConstantIndex { .. } |
// assigning to P[i] requires P to be valid. // assigning to P[i] requires P to be valid.
ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) => ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
@ -2179,6 +2180,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
| ProjectionElem::Index(..) | ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. } | ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } | ProjectionElem::Subslice { .. }
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Downcast(..) => { | ProjectionElem::Downcast(..) => {
let upvar_field_projection = self.is_upvar_field_projection(place); let upvar_field_projection = self.is_upvar_field_projection(place);
if let Some(field) = upvar_field_projection { if let Some(field) = upvar_field_projection {

View file

@ -255,6 +255,7 @@ fn place_components_conflict<'tcx>(
| (ProjectionElem::Index { .. }, _, _) | (ProjectionElem::Index { .. }, _, _)
| (ProjectionElem::ConstantIndex { .. }, _, _) | (ProjectionElem::ConstantIndex { .. }, _, _)
| (ProjectionElem::Subslice { .. }, _, _) | (ProjectionElem::Subslice { .. }, _, _)
| (ProjectionElem::OpaqueCast { .. }, _, _)
| (ProjectionElem::Downcast { .. }, _, _) => { | (ProjectionElem::Downcast { .. }, _, _) => {
// Recursive case. This can still be disjoint on a // Recursive case. This can still be disjoint on a
// further iteration if this a shallow access and // further iteration if this a shallow access and
@ -322,6 +323,17 @@ fn place_projection_conflict<'tcx>(
debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF"); debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
Overlap::EqualOrDisjoint Overlap::EqualOrDisjoint
} }
(ProjectionElem::OpaqueCast(v1), ProjectionElem::OpaqueCast(v2)) => {
if v1 == v2 {
// same type - recur.
debug!("place_element_conflict: DISJOINT-OR-EQ-OPAQUE");
Overlap::EqualOrDisjoint
} else {
// Different types. Disjoint!
debug!("place_element_conflict: DISJOINT-OPAQUE");
Overlap::Disjoint
}
}
(ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => { (ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
if f1 == f2 { if f1 == f2 {
// same field (e.g., `a.y` vs. `a.y`) - recur. // same field (e.g., `a.y` vs. `a.y`) - recur.
@ -525,6 +537,7 @@ fn place_projection_conflict<'tcx>(
| ProjectionElem::Field(..) | ProjectionElem::Field(..)
| ProjectionElem::Index(..) | ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. } | ProjectionElem::ConstantIndex { .. }
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Subslice { .. } | ProjectionElem::Subslice { .. }
| ProjectionElem::Downcast(..), | ProjectionElem::Downcast(..),
_, _,

View file

@ -81,6 +81,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
} }
ProjectionElem::Downcast(..) ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. } | ProjectionElem::Subslice { .. }
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::ConstantIndex { .. } | ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Index(_) => { | ProjectionElem::Index(_) => {
cursor = cursor_base; cursor = cursor_base;

View file

@ -790,6 +790,19 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
} }
PlaceTy::from_ty(fty) PlaceTy::from_ty(fty)
} }
ProjectionElem::OpaqueCast(ty) => {
let ty = self.sanitize_type(place, ty);
let ty = self.cx.normalize(ty, location);
self.cx
.eq_types(
base.ty,
ty,
location.to_locations(),
ConstraintCategory::TypeAnnotation,
)
.unwrap();
PlaceTy::from_ty(ty)
}
} }
} }
@ -1195,10 +1208,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
tcx, tcx,
self.param_env, self.param_env,
proj, proj,
|this, field, ()| { |this, field, _| {
let ty = this.field_ty(tcx, field); let ty = this.field_ty(tcx, field);
self.normalize(ty, locations) self.normalize(ty, locations)
}, },
|_, _| unreachable!(),
); );
curr_projected_ty = projected_ty; curr_projected_ty = projected_ty;
} }
@ -2493,6 +2507,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
} }
ProjectionElem::Field(..) ProjectionElem::Field(..)
| ProjectionElem::Downcast(..) | ProjectionElem::Downcast(..)
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::Index(..) | ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. } | ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => { | ProjectionElem::Subslice { .. } => {

View file

@ -825,6 +825,7 @@ pub(crate) fn codegen_place<'tcx>(
cplace = cplace.place_deref(fx); cplace = cplace.place_deref(fx);
} }
} }
PlaceElem::OpaqueCast(ty) => cplace = cplace.place_opaque_cast(fx, ty),
PlaceElem::Field(field, _ty) => { PlaceElem::Field(field, _ty) => {
cplace = cplace.place_field(fx, field); cplace = cplace.place_field(fx, field);
} }

View file

@ -615,6 +615,14 @@ impl<'tcx> CPlace<'tcx> {
} }
} }
pub(crate) fn place_opaque_cast(
self,
fx: &mut FunctionCx<'_, '_, 'tcx>,
ty: Ty<'tcx>,
) -> CPlace<'tcx> {
CPlace { inner: self.inner, layout: fx.layout_of(ty) }
}
pub(crate) fn place_field( pub(crate) fn place_field(
self, self,
fx: &mut FunctionCx<'_, '_, 'tcx>, fx: &mut FunctionCx<'_, '_, 'tcx>,

View file

@ -411,6 +411,21 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
downcast downcast
} }
pub fn project_type<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
&self,
bx: &mut Bx,
ty: Ty<'tcx>,
) -> Self {
let mut downcast = *self;
downcast.layout = bx.cx().layout_of(ty);
// Cast to the appropriate type.
let variant_ty = bx.cx().backend_type(downcast.layout);
downcast.llval = bx.pointercast(downcast.llval, bx.cx().type_ptr_to(variant_ty));
downcast
}
pub fn storage_live<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx) { pub fn storage_live<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx) {
bx.lifetime_start(self.llval, self.layout.size); bx.lifetime_start(self.llval, self.layout.size);
} }
@ -459,6 +474,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::ProjectionElem::Field(ref field, _) => { mir::ProjectionElem::Field(ref field, _) => {
cg_base.project_field(bx, field.index()) cg_base.project_field(bx, field.index())
} }
mir::ProjectionElem::OpaqueCast(ty) => cg_base.project_type(bx, ty),
mir::ProjectionElem::Index(index) => { mir::ProjectionElem::Index(index) => {
let index = &mir::Operand::Copy(mir::Place::from(index)); let index = &mir::Operand::Copy(mir::Place::from(index));
let index = self.codegen_operand(bx, index); let index = self.codegen_operand(bx, index);

View file

@ -436,7 +436,7 @@ fn valtree_into_mplace<'tcx>(
let offset = place_adjusted.layout.fields.offset(i); let offset = place_adjusted.layout.fields.offset(i);
place place
.offset( .offset_with_meta(
offset, offset,
MemPlaceMeta::Meta(Scalar::from_machine_usize( MemPlaceMeta::Meta(Scalar::from_machine_usize(
num_elems as u64, num_elems as u64,

View file

@ -297,7 +297,7 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> {
} }
} }
pub fn offset( pub fn offset_with_meta(
&self, &self,
offset: Size, offset: Size,
meta: MemPlaceMeta<Tag>, meta: MemPlaceMeta<Tag>,
@ -305,7 +305,7 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> {
cx: &impl HasDataLayout, cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> { ) -> InterpResult<'tcx, Self> {
match self.try_as_mplace() { match self.try_as_mplace() {
Ok(mplace) => Ok(mplace.offset(offset, meta, layout, cx)?.into()), Ok(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, cx)?.into()),
Err(imm) => { Err(imm) => {
assert!( assert!(
matches!(*imm, Immediate::Uninit), matches!(*imm, Immediate::Uninit),
@ -317,6 +317,16 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> {
} }
} }
} }
pub fn offset(
&self,
offset: Size,
layout: TyAndLayout<'tcx>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> {
assert!(!layout.is_unsized());
self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
}
} }
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {

View file

@ -163,7 +163,7 @@ impl<Tag: Provenance> MemPlace<Tag> {
} }
#[inline] #[inline]
pub fn offset<'tcx>( pub fn offset_with_meta<'tcx>(
self, self,
offset: Size, offset: Size,
meta: MemPlaceMeta<Tag>, meta: MemPlaceMeta<Tag>,
@ -199,7 +199,7 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> {
} }
#[inline] #[inline]
pub fn offset( pub fn offset_with_meta(
&self, &self,
offset: Size, offset: Size,
meta: MemPlaceMeta<Tag>, meta: MemPlaceMeta<Tag>,
@ -207,12 +207,22 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> {
cx: &impl HasDataLayout, cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> { ) -> InterpResult<'tcx, Self> {
Ok(MPlaceTy { Ok(MPlaceTy {
mplace: self.mplace.offset(offset, meta, cx)?, mplace: self.mplace.offset_with_meta(offset, meta, cx)?,
align: self.align.restrict_for_offset(offset), align: self.align.restrict_for_offset(offset),
layout, layout,
}) })
} }
pub fn offset(
&self,
offset: Size,
layout: TyAndLayout<'tcx>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> {
assert!(!layout.is_unsized());
self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
}
#[inline] #[inline]
pub fn from_aligned_ptr(ptr: Pointer<Option<Tag>>, layout: TyAndLayout<'tcx>) -> Self { pub fn from_aligned_ptr(ptr: Pointer<Option<Tag>>, layout: TyAndLayout<'tcx>) -> Self {
MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi } MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi }

View file

@ -63,7 +63,7 @@ where
// We do not look at `base.layout.align` nor `field_layout.align`, unlike // We do not look at `base.layout.align` nor `field_layout.align`, unlike
// codegen -- mostly to see if we can get away with that // codegen -- mostly to see if we can get away with that
base.offset(offset, meta, field_layout, self) base.offset_with_meta(offset, meta, field_layout, self)
} }
/// Gets the place of a field inside the place, and also the field's type. /// Gets the place of a field inside the place, and also the field's type.
@ -193,9 +193,7 @@ where
let offset = stride * index; // `Size` multiplication let offset = stride * index; // `Size` multiplication
// All fields have the same layout. // All fields have the same layout.
let field_layout = base.layout.field(self, 0); let field_layout = base.layout.field(self, 0);
assert!(!field_layout.is_unsized()); base.offset(offset, field_layout, self)
base.offset(offset, MemPlaceMeta::None, field_layout, self)
} }
_ => span_bug!( _ => span_bug!(
self.cur_span(), self.cur_span(),
@ -215,10 +213,10 @@ where
let abi::FieldsShape::Array { stride, .. } = base.layout.fields else { let abi::FieldsShape::Array { stride, .. } = base.layout.fields else {
span_bug!(self.cur_span(), "operand_array_fields: expected an array layout"); span_bug!(self.cur_span(), "operand_array_fields: expected an array layout");
}; };
let layout = base.layout.field(self, 0); let field_layout = base.layout.field(self, 0);
let dl = &self.tcx.data_layout; let dl = &self.tcx.data_layout;
// `Size` multiplication // `Size` multiplication
Ok((0..len).map(move |i| base.offset(stride * i, MemPlaceMeta::None, layout, dl))) Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl)))
} }
/// Index into an array. /// Index into an array.
@ -326,7 +324,7 @@ where
} }
}; };
let layout = self.layout_of(ty)?; let layout = self.layout_of(ty)?;
base.offset(from_offset, meta, layout, self) base.offset_with_meta(from_offset, meta, layout, self)
} }
pub fn place_subslice( pub fn place_subslice(
@ -351,6 +349,11 @@ where
) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
use rustc_middle::mir::ProjectionElem::*; use rustc_middle::mir::ProjectionElem::*;
Ok(match proj_elem { Ok(match proj_elem {
OpaqueCast(ty) => {
let mut place = *base;
place.layout = self.layout_of(ty)?;
place
}
Field(field, _) => self.place_field(base, field.index())?, Field(field, _) => self.place_field(base, field.index())?,
Downcast(_, variant) => self.place_downcast(base, variant)?, Downcast(_, variant) => self.place_downcast(base, variant)?,
Deref => self.deref_operand(&self.place_to_op(base)?)?.into(), Deref => self.deref_operand(&self.place_to_op(base)?)?.into(),
@ -375,6 +378,11 @@ where
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
use rustc_middle::mir::ProjectionElem::*; use rustc_middle::mir::ProjectionElem::*;
Ok(match proj_elem { Ok(match proj_elem {
OpaqueCast(ty) => {
let mut op = *base;
op.layout = self.layout_of(ty)?;
op
}
Field(field, _) => self.operand_field(base, field.index())?, Field(field, _) => self.operand_field(base, field.index())?,
Downcast(_, variant) => self.operand_downcast(base, variant)?, Downcast(_, variant) => self.operand_downcast(base, variant)?,
Deref => self.deref_operand(base)?.into(), Deref => self.deref_operand(base)?.into(),

View file

@ -853,7 +853,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
self.visit_scalar(scalar, scalar_layout)?; self.visit_scalar(scalar, scalar_layout)?;
} }
Abi::ScalarPair(a_layout, b_layout) => { Abi::ScalarPair(a_layout, b_layout) => {
// We would validate these things as we descend into the fields, // There is no `rustc_layout_scalar_valid_range_start` for pairs, so
// we would validate these things as we descend into the fields,
// but that can miss bugs in layout computation. Layout computation // but that can miss bugs in layout computation. Layout computation
// is subtle due to enums having ScalarPair layout, where one field // is subtle due to enums having ScalarPair layout, where one field
// is the discriminant. // is the discriminant.
@ -867,7 +868,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
} }
Abi::Vector { .. } => { Abi::Vector { .. } => {
// No checks here, we assume layout computation gets this right. // No checks here, we assume layout computation gets this right.
// (This is harder to check since Miri does not represent these as `Immediate`.) // (This is harder to check since Miri does not represent these as `Immediate`. We
// also cannot use field projections since this might be a newtype around a vector.)
} }
Abi::Aggregate { .. } => { Abi::Aggregate { .. } => {
// Nothing to do. // Nothing to do.

View file

@ -8,23 +8,33 @@ use rustc_target::abi::{FieldsShape, VariantIdx, Variants};
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use super::{InterpCx, MPlaceTy, Machine, OpTy}; use super::{InterpCx, MPlaceTy, Machine, OpTy, PlaceTy};
// A thing that we can project into, and that has a layout. /// A thing that we can project into, and that has a layout.
// This wouldn't have to depend on `Machine` but with the current type inference, /// This wouldn't have to depend on `Machine` but with the current type inference,
// that's just more convenient to work with (avoids repeating all the `Machine` bounds). /// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy { pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy {
/// Gets this value's layout. /// Gets this value's layout.
fn layout(&self) -> TyAndLayout<'tcx>; fn layout(&self) -> TyAndLayout<'tcx>;
/// Makes this into an `OpTy`. /// Makes this into an `OpTy`, in a cheap way that is good for reading.
fn to_op(&self, ecx: &InterpCx<'mir, 'tcx, M>) fn to_op_for_read(
-> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; &self,
ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>;
/// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections.
fn to_op_for_proj(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
self.to_op_for_read(ecx)
}
/// Creates this from an `OpTy`. /// Creates this from an `OpTy`.
/// ///
/// If `to_op` only ever produces `Indirect` operands, then this one is definitely `Indirect`. /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
fn from_op(mplace: OpTy<'tcx, M::PointerTag>) -> Self; fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self;
/// Projects to the given enum variant. /// Projects to the given enum variant.
fn project_downcast( fn project_downcast(
@ -41,8 +51,50 @@ pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy {
) -> InterpResult<'tcx, Self>; ) -> InterpResult<'tcx, Self>;
} }
// Operands and memory-places are both values. /// A thing that we can project into given *mutable* access to `ecx`, and that has a layout.
// Places in general are not due to `place_field` having to do `force_allocation`. /// This wouldn't have to depend on `Machine` but with the current type inference,
/// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
pub trait ValueMut<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy {
/// Gets this value's layout.
fn layout(&self) -> TyAndLayout<'tcx>;
/// Makes this into an `OpTy`, in a cheap way that is good for reading.
fn to_op_for_read(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>;
/// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections.
fn to_op_for_proj(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>;
/// Creates this from an `OpTy`.
///
/// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self;
/// Projects to the given enum variant.
fn project_downcast(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
variant: VariantIdx,
) -> InterpResult<'tcx, Self>;
/// Projects to the n-th field.
fn project_field(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
field: usize,
) -> InterpResult<'tcx, Self>;
}
// We cannot have a general impl which shows that Value implies ValueMut. (When we do, it says we
// cannot `impl ValueMut for PlaceTy` because some downstream crate could `impl Value for PlaceTy`.)
// So we have some copy-paste here. (We could have a macro but since we only have 2 types with this
// double-impl, that would barely make the code shorter, if at all.)
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> { impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> {
#[inline(always)] #[inline(always)]
fn layout(&self) -> TyAndLayout<'tcx> { fn layout(&self) -> TyAndLayout<'tcx> {
@ -50,7 +102,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc
} }
#[inline(always)] #[inline(always)]
fn to_op( fn to_op_for_read(
&self, &self,
_ecx: &InterpCx<'mir, 'tcx, M>, _ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
@ -58,8 +110,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc
} }
#[inline(always)] #[inline(always)]
fn from_op(op: OpTy<'tcx, M::PointerTag>) -> Self { fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self {
op *op
} }
#[inline(always)] #[inline(always)]
@ -81,6 +133,54 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc
} }
} }
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
for OpTy<'tcx, M::PointerTag>
{
#[inline(always)]
fn layout(&self) -> TyAndLayout<'tcx> {
self.layout
}
#[inline(always)]
fn to_op_for_read(
&self,
_ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
Ok(*self)
}
#[inline(always)]
fn to_op_for_proj(
&self,
_ecx: &mut InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
Ok(*self)
}
#[inline(always)]
fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self {
*op
}
#[inline(always)]
fn project_downcast(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
variant: VariantIdx,
) -> InterpResult<'tcx, Self> {
ecx.operand_downcast(self, variant)
}
#[inline(always)]
fn project_field(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
field: usize,
) -> InterpResult<'tcx, Self> {
ecx.operand_field(self, field)
}
}
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
for MPlaceTy<'tcx, M::PointerTag> for MPlaceTy<'tcx, M::PointerTag>
{ {
@ -90,7 +190,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
} }
#[inline(always)] #[inline(always)]
fn to_op( fn to_op_for_read(
&self, &self,
_ecx: &InterpCx<'mir, 'tcx, M>, _ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
@ -98,8 +198,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
} }
#[inline(always)] #[inline(always)]
fn from_op(op: OpTy<'tcx, M::PointerTag>) -> Self { fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self {
// assert is justified because our `to_op` only ever produces `Indirect` operands. // assert is justified because our `to_op_for_read` only ever produces `Indirect` operands.
op.assert_mem_place() op.assert_mem_place()
} }
@ -122,11 +222,111 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
} }
} }
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
for MPlaceTy<'tcx, M::PointerTag>
{
#[inline(always)]
fn layout(&self) -> TyAndLayout<'tcx> {
self.layout
}
#[inline(always)]
fn to_op_for_read(
&self,
_ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
Ok(self.into())
}
#[inline(always)]
fn to_op_for_proj(
&self,
_ecx: &mut InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
Ok(self.into())
}
#[inline(always)]
fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self {
// assert is justified because our `to_op_for_proj` only ever produces `Indirect` operands.
op.assert_mem_place()
}
#[inline(always)]
fn project_downcast(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
variant: VariantIdx,
) -> InterpResult<'tcx, Self> {
ecx.mplace_downcast(self, variant)
}
#[inline(always)]
fn project_field(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
field: usize,
) -> InterpResult<'tcx, Self> {
ecx.mplace_field(self, field)
}
}
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
for PlaceTy<'tcx, M::PointerTag>
{
#[inline(always)]
fn layout(&self) -> TyAndLayout<'tcx> {
self.layout
}
#[inline(always)]
fn to_op_for_read(
&self,
ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
// We `force_allocation` here so that `from_op` below can work.
ecx.place_to_op(self)
}
#[inline(always)]
fn to_op_for_proj(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
// We `force_allocation` here so that `from_op` below can work.
Ok(ecx.force_allocation(self)?.into())
}
#[inline(always)]
fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self {
// assert is justified because our `to_op` only ever produces `Indirect` operands.
op.assert_mem_place().into()
}
#[inline(always)]
fn project_downcast(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
variant: VariantIdx,
) -> InterpResult<'tcx, Self> {
ecx.place_downcast(self, variant)
}
#[inline(always)]
fn project_field(
&self,
ecx: &mut InterpCx<'mir, 'tcx, M>,
field: usize,
) -> InterpResult<'tcx, Self> {
ecx.place_field(self, field)
}
}
macro_rules! make_value_visitor { macro_rules! make_value_visitor {
($visitor_trait_name:ident, $($mutability:ident)?) => { ($visitor_trait:ident, $value_trait:ident, $($mutability:ident)?) => {
// How to traverse a value and what to do when we are at the leaves. // How to traverse a value and what to do when we are at the leaves.
pub trait $visitor_trait_name<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { pub trait $visitor_trait<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
type V: Value<'mir, 'tcx, M>; type V: $value_trait<'mir, 'tcx, M>;
/// The visitor must have an `InterpCx` in it. /// The visitor must have an `InterpCx` in it.
fn ecx(&$($mutability)? self) fn ecx(&$($mutability)? self)
@ -215,19 +415,20 @@ macro_rules! make_value_visitor {
} }
fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx> fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx>
{ {
trace!("walk_value: type: {}", v.layout().ty); let ty = v.layout().ty;
trace!("walk_value: type: {ty}");
// Special treatment for special types, where the (static) layout is not sufficient. // Special treatment for special types, where the (static) layout is not sufficient.
match *v.layout().ty.kind() { match *ty.kind() {
// If it is a trait object, switch to the real type that was used to create it. // If it is a trait object, switch to the real type that was used to create it.
ty::Dynamic(..) => { ty::Dynamic(..) => {
// unsized values are never immediate, so we can assert_mem_place // unsized values are never immediate, so we can assert_mem_place
let op = v.to_op(self.ecx())?; let op = v.to_op_for_read(self.ecx())?;
let dest = op.assert_mem_place(); let dest = op.assert_mem_place();
let inner = self.ecx().unpack_dyn_trait(&dest)?.1; let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.1;
trace!("walk_value: dyn object layout: {:#?}", inner.layout); trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
// recurse with the inner type // recurse with the inner type
return self.visit_field(&v, 0, &Value::from_op(inner.into())); return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into()));
}, },
// Slices do not need special handling here: they have `Array` field // Slices do not need special handling here: they have `Array` field
// placement with length 0, so we enter the `Array` case below which // placement with length 0, so we enter the `Array` case below which
@ -278,10 +479,10 @@ macro_rules! make_value_visitor {
// Visit the fields of this value. // Visit the fields of this value.
match v.layout().fields { match v.layout().fields {
FieldsShape::Primitive => {}, FieldsShape::Primitive => {}
FieldsShape::Union(fields) => { FieldsShape::Union(fields) => {
self.visit_union(v, fields)?; self.visit_union(v, fields)?;
}, }
FieldsShape::Arbitrary { ref offsets, .. } => { FieldsShape::Arbitrary { ref offsets, .. } => {
// FIXME: We collect in a vec because otherwise there are lifetime // FIXME: We collect in a vec because otherwise there are lifetime
// errors: Projecting to a field needs access to `ecx`. // errors: Projecting to a field needs access to `ecx`.
@ -291,16 +492,17 @@ macro_rules! make_value_visitor {
}) })
.collect(); .collect();
self.visit_aggregate(v, fields.into_iter())?; self.visit_aggregate(v, fields.into_iter())?;
}, }
FieldsShape::Array { .. } => { FieldsShape::Array { .. } => {
// Let's get an mplace first. // Let's get an mplace (or immediate) first.
let op = v.to_op(self.ecx())?; // This might `force_allocate` if `v` is a `PlaceTy`, but `place_index` does that anyway.
let op = v.to_op_for_proj(self.ecx())?;
// Now we can go over all the fields. // Now we can go over all the fields.
// This uses the *run-time length*, i.e., if we are a slice, // This uses the *run-time length*, i.e., if we are a slice,
// the dynamic info from the metadata is used. // the dynamic info from the metadata is used.
let iter = self.ecx().operand_array_fields(&op)? let iter = self.ecx().operand_array_fields(&op)?
.map(|f| f.and_then(|f| { .map(|f| f.and_then(|f| {
Ok(Value::from_op(f)) Ok($value_trait::from_op(&f))
})); }));
self.visit_aggregate(v, iter)?; self.visit_aggregate(v, iter)?;
} }
@ -310,7 +512,7 @@ macro_rules! make_value_visitor {
// If this is a multi-variant layout, find the right variant and proceed // If this is a multi-variant layout, find the right variant and proceed
// with *its* fields. // with *its* fields.
Variants::Multiple { .. } => { Variants::Multiple { .. } => {
let op = v.to_op(self.ecx())?; let op = v.to_op_for_read(self.ecx())?;
let idx = self.read_discriminant(&op)?; let idx = self.read_discriminant(&op)?;
let inner = v.project_downcast(self.ecx(), idx)?; let inner = v.project_downcast(self.ecx(), idx)?;
trace!("walk_value: variant layout: {:#?}", inner.layout()); trace!("walk_value: variant layout: {:#?}", inner.layout());
@ -325,5 +527,5 @@ macro_rules! make_value_visitor {
} }
} }
make_value_visitor!(ValueVisitor,); make_value_visitor!(ValueVisitor, Value,);
make_value_visitor!(MutValueVisitor, mut); make_value_visitor!(MutValueVisitor, ValueMut, mut);

View file

@ -652,6 +652,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
ProjectionElem::ConstantIndex { .. } ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Downcast(..) | ProjectionElem::Downcast(..)
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::Subslice { .. } | ProjectionElem::Subslice { .. }
| ProjectionElem::Field(..) | ProjectionElem::Field(..)
| ProjectionElem::Index(_) => {} | ProjectionElem::Index(_) => {}

View file

@ -316,6 +316,7 @@ where
ProjectionElem::Deref ProjectionElem::Deref
| ProjectionElem::Field(_, _) | ProjectionElem::Field(_, _)
| ProjectionElem::OpaqueCast(_)
| ProjectionElem::ConstantIndex { .. } | ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } | ProjectionElem::Subslice { .. }
| ProjectionElem::Downcast(_, _) | ProjectionElem::Downcast(_, _)

View file

@ -361,7 +361,7 @@ impl<'tcx> Validator<'_, 'tcx> {
return Err(Unpromotable); return Err(Unpromotable);
} }
} }
ProjectionElem::Downcast(..) => { ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => {
return Err(Unpromotable); return Err(Unpromotable);
} }

View file

@ -0,0 +1,151 @@
-passes-previously-accepted =
this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-passes-see-issue =
see issue #{$issue} <https://github.com/rust-lang/rust/issues/{$issue}> for more information
passes-outer-crate-level-attr =
crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
passes-inner-crate-level-attr =
crate-level attribute should be in the root module
passes-ignored-attr-with-macro = `#[{$sym}]` is ignored on struct fields, match arms and macro defs
.warn = {-passes-previously-accepted}
.note = {-passes-see-issue(issue: "80564")}
passes-ignored-attr = `#[{$sym}]` is ignored on struct fields and match arms
.warn = {-passes-previously-accepted}
.note = {-passes-see-issue(issue: "80564")}
passes-inline-ignored-function-prototype = `#[inline]` is ignored on function prototypes
passes-inline-ignored-constants = `#[inline]` is ignored on constants
.warn = {-passes-previously-accepted}
.note = {-passes-see-issue(issue: "65833")}
passes-inline-not-fn-or-closure = attribute should be applied to function or closure
.label = not a function or closure
passes-no-coverage-ignored-function-prototype = `#[no_coverage]` is ignored on function prototypes
passes-no-coverage-propagate =
`#[no_coverage]` does not propagate into items and must be applied to the contained functions directly
passes-no-coverage-fn-defn = `#[no_coverage]` may only be applied to function definitions
passes-no-coverage-not-coverable = `#[no_coverage]` must be applied to coverable code
.label = not coverable code
passes-should-be-applied-to-fn = attribute should be applied to a function definition
.label = not a function definition
passes-naked-tracked-caller = cannot use `#[track_caller]` with `#[naked]`
passes-should-be-applied-to-struct-enum = attribute should be applied to a struct or enum
.label = not a struct or enum
passes-should-be-applied-to-trait = attribute should be applied to a trait
.label = not a trait
passes-target-feature-on-statement = {passes-should-be-applied-to-fn}
.warn = {-passes-previously-accepted}
.label = {passes-should-be-applied-to-fn.label}
passes-should-be-applied-to-static = attribute should be applied to a static
.label = not a static
passes-doc-expect-str = doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")]
passes-doc-alias-empty = {$attr_str} attribute cannot have empty value
passes-doc-alias-bad-char = {$char_} character isn't allowed in {$attr_str}
passes-doc-alias-start-end = {$attr_str} cannot start or end with ' '
passes-doc-alias-bad-location = {$attr_str} isn't allowed on {$location}
passes-doc-alias-not-an-alias = {$attr_str} is the same as the item's name
passes-doc-alias-duplicated = doc alias is duplicated
.label = first defined here
passes-doc-alias-not-string-literal = `#[doc(alias("a"))]` expects string literals
passes-doc-alias-malformed =
doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
passes-doc-keyword-empty-mod = `#[doc(keyword = "...")]` should be used on empty modules
passes-doc-keyword-not-mod = `#[doc(keyword = "...")]` should be used on modules
passes-doc-keyword-invalid-ident = `{$doc_keyword}` is not a valid identifier
passes-doc-tuple-variadic-not-first =
`#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity
passes-doc-keyword-only-impl = `#[doc(keyword = "...")]` should be used on impl blocks
passes-doc-inline-conflict-first = this attribute...
passes-doc-inline-conflict-second = ...conflicts with this attribute
passes-doc-inline-conflict = conflicting doc inlining attributes
.help = remove one of the conflicting attributes
passes-doc-inline-only-use = this attribute can only be applied to a `use` item
.label = only applicable on `use` items
.not-a-use-item-label = not a `use` item
.note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline> for more information
passes-doc-attr-not-crate-level =
`#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute
passes-attr-crate-level = this attribute can only be applied at the crate level
.suggestion = to apply to the crate, use an inner attribute
.help = to apply to the crate, use an inner attribute
.note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> for more information
passes-doc-test-unknown = unknown `doc(test)` attribute `{$path}`
passes-doc-test-takes-list = `#[doc(test(...)]` takes a list of attributes
passes-doc-primitive = `doc(primitive)` should never have been stable
passes-doc-test-unknown-any = unknown `doc` attribute `{$path}`
passes-doc-test-unknown-spotlight = unknown `doc` attribute `{$path}`
.note = `doc(spotlight)` was renamed to `doc(notable_trait)`
.suggestion = use `notable_trait` instead
.no-op-note = `doc(spotlight)` is now a no-op
passes-doc-test-unknown-include = unknown `doc` attribute `{$path}`
.suggestion = use `doc = include_str!` instead
passes-doc-invalid = invalid `doc` attribute
passes-pass-by-value = `pass_by_value` attribute should be applied to a struct, enum or type alias
.label = is not a struct, enum or type alias
passes-allow-incoherent-impl =
`rustc_allow_incoherent_impl` attribute should be applied to impl items.
.label = the only currently supported targets are inherent methods
passes-has-incoherent-inherent-impl =
`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.
.label = only adts, extern types and traits are supported
passes-must-use-async =
`must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within
.label = this attribute does nothing, the `Future`s returned by async functions are already `must_use`
passes-must-use-no-effect = `#[must_use]` has no effect when applied to {$article} {$target}
passes-must-not-suspend = `must_not_suspend` attribute should be applied to a struct, enum, or trait
.label = is not a struct, enum, or trait
passes-cold = {passes-should-be-applied-to-fn}
.warn = {-passes-previously-accepted}
.label = {passes-should-be-applied-to-fn.label}
passes-link = attribute should be applied to an `extern` block with non-Rust ABI
.warn = {-passes-previously-accepted}
.label = not an `extern` block

View file

@ -10,3 +10,12 @@ privacy-unnamed-item-is-private = {$kind} is private
privacy-in-public-interface = {$vis_descr} {$kind} `{$descr}` in public interface privacy-in-public-interface = {$vis_descr} {$kind} `{$descr}` in public interface
.label = can't leak {$vis_descr} {$kind} .label = can't leak {$vis_descr} {$kind}
.visibility-label = `{$descr}` declared as {$vis_descr} .visibility-label = `{$descr}` declared as {$vis_descr}
privacy-from-private-dep-in-public-interface =
{$kind} `{$descr}` from private dependency '{$krate}' in public interface
private-in-public-lint =
{$vis_descr} {$kind} `{$descr}` in public interface (error {$kind ->
[trait] E0445
*[other] E0446
})

View file

@ -37,6 +37,7 @@ fluent_messages! {
expand => "../locales/en-US/expand.ftl", expand => "../locales/en-US/expand.ftl",
lint => "../locales/en-US/lint.ftl", lint => "../locales/en-US/lint.ftl",
parser => "../locales/en-US/parser.ftl", parser => "../locales/en-US/parser.ftl",
passes => "../locales/en-US/passes.ftl",
privacy => "../locales/en-US/privacy.ftl", privacy => "../locales/en-US/privacy.ftl",
typeck => "../locales/en-US/typeck.ftl", typeck => "../locales/en-US/typeck.ftl",
} }

View file

@ -40,6 +40,35 @@ pub trait IntoDiagnosticArg {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>; fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>;
} }
macro_rules! into_diagnostic_arg_using_display {
($( $ty:ty ),+ $(,)?) => {
$(
impl IntoDiagnosticArg for $ty {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
self.to_string().into_diagnostic_arg()
}
}
)+
}
}
into_diagnostic_arg_using_display!(
i8,
u8,
i16,
u16,
i32,
u32,
i64,
u64,
i128,
u128,
std::num::NonZeroU32,
hir::Target,
Edition,
Ident,
);
impl IntoDiagnosticArg for bool { impl IntoDiagnosticArg for bool {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
if self { if self {
@ -50,81 +79,9 @@ impl IntoDiagnosticArg for bool {
} }
} }
impl IntoDiagnosticArg for i8 { impl IntoDiagnosticArg for char {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self.to_string())) DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self)))
}
}
impl IntoDiagnosticArg for u8 {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
}
}
impl IntoDiagnosticArg for i16 {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
}
}
impl IntoDiagnosticArg for u16 {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
}
}
impl IntoDiagnosticArg for i32 {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
}
}
impl IntoDiagnosticArg for u32 {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
}
}
impl IntoDiagnosticArg for i64 {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
}
}
impl IntoDiagnosticArg for u64 {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
}
}
impl IntoDiagnosticArg for i128 {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
}
}
impl IntoDiagnosticArg for u128 {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
}
}
impl IntoDiagnosticArg for String {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self))
}
}
impl IntoDiagnosticArg for std::num::NonZeroU32 {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
}
}
impl IntoDiagnosticArg for Edition {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
} }
} }
@ -134,15 +91,15 @@ impl IntoDiagnosticArg for Symbol {
} }
} }
impl IntoDiagnosticArg for Ident { impl<'a> IntoDiagnosticArg for &'a str {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
self.to_string().into_diagnostic_arg() self.to_string().into_diagnostic_arg()
} }
} }
impl<'a> IntoDiagnosticArg for &'a str { impl IntoDiagnosticArg for String {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
self.to_string().into_diagnostic_arg() DiagnosticArgValue::Str(Cow::Owned(self))
} }
} }
@ -496,7 +453,7 @@ impl Diagnostic {
self self
} }
pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self { pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self {
self.highlighted_note(vec![ self.highlighted_note(vec![
(format!("`{}` from trait: `", name), Style::NoStyle), (format!("`{}` from trait: `", name), Style::NoStyle),
(signature, Style::Highlight), (signature, Style::Highlight),

View file

@ -595,6 +595,7 @@ macro_rules! error_code {
pub struct LintDiagnosticBuilder<'a, G: EmissionGuarantee>(DiagnosticBuilder<'a, G>); pub struct LintDiagnosticBuilder<'a, G: EmissionGuarantee>(DiagnosticBuilder<'a, G>);
impl<'a, G: EmissionGuarantee> LintDiagnosticBuilder<'a, G> { impl<'a, G: EmissionGuarantee> LintDiagnosticBuilder<'a, G> {
#[rustc_lint_diagnostics]
/// Return the inner `DiagnosticBuilder`, first setting the primary message to `msg`. /// Return the inner `DiagnosticBuilder`, first setting the primary message to `msg`.
pub fn build(mut self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'a, G> { pub fn build(mut self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'a, G> {
self.0.set_primary_message(msg); self.0.set_primary_message(msg);

View file

@ -218,10 +218,9 @@ pub fn default_submod_path<'a>(
"" ""
}; };
let mod_name = ident.name.to_string(); let default_path_str = format!("{}{}.rs", relative_prefix, ident.name);
let default_path_str = format!("{}{}.rs", relative_prefix, mod_name);
let secondary_path_str = let secondary_path_str =
format!("{}{}{}mod.rs", relative_prefix, mod_name, path::MAIN_SEPARATOR); format!("{}{}{}mod.rs", relative_prefix, ident.name, path::MAIN_SEPARATOR);
let default_path = dir_path.join(&default_path_str); let default_path = dir_path.join(&default_path_str);
let secondary_path = dir_path.join(&secondary_path_str); let secondary_path = dir_path.join(&secondary_path_str);
let default_exists = sess.source_map().file_exists(&default_path); let default_exists = sess.source_map().file_exists(&default_path);

View file

@ -138,7 +138,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPr
if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = if let TypeVariableOriginKind::TypeParameterDefinition(name, _) =
infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind
{ {
Some(name.to_string()) Some(name)
} else { } else {
None None
} }
@ -151,7 +151,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPr
if let ConstVariableOriginKind::ConstParameterDefinition(name, _) = if let ConstVariableOriginKind::ConstParameterDefinition(name, _) =
infcx.inner.borrow_mut().const_unification_table().probe_value(ct_vid).origin.kind infcx.inner.borrow_mut().const_unification_table().probe_value(ct_vid).origin.kind
{ {
return Some(name.to_string()); return Some(name);
} else { } else {
None None
} }

View file

@ -59,7 +59,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
return DiagnosticDeriveError::ErrorHandled.to_compile_error(); return DiagnosticDeriveError::ErrorHandled.to_compile_error();
} }
(Some(DiagnosticDeriveKind::Lint), _) => { (Some(DiagnosticDeriveKind::Lint), _) => {
span_err(span, "only `#[error(..)]` and `#[warn(..)]` are supported") span_err(span, "only `#[error(..)]` and `#[warning(..)]` are supported")
.help("use the `#[error(...)]` attribute to create a error") .help("use the `#[error(...)]` attribute to create a error")
.emit(); .emit();
return DiagnosticDeriveError::ErrorHandled.to_compile_error(); return DiagnosticDeriveError::ErrorHandled.to_compile_error();

View file

@ -8,12 +8,13 @@ use crate::diagnostics::utils::{
report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path, report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
}; };
use proc_macro2::{Ident, TokenStream}; use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use std::collections::HashMap; use std::collections::HashMap;
use std::str::FromStr; use std::str::FromStr;
use syn::{ use syn::{
parse_quote, spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type, parse_quote, spanned::Spanned, Attribute, Field, Meta, MetaList, MetaNameValue, NestedMeta,
Path, Type,
}; };
use synstructure::{BindingInfo, Structure}; use synstructure::{BindingInfo, Structure};
@ -80,8 +81,8 @@ impl DiagnosticDeriveBuilder {
} }
pub fn body<'s>(&mut self, structure: &mut Structure<'s>) -> (TokenStream, TokenStream) { pub fn body<'s>(&mut self, structure: &mut Structure<'s>) -> (TokenStream, TokenStream) {
// Keep track of which fields are subdiagnostics or have no attributes. // Keep track of which fields need to be handled with a by-move binding.
let mut subdiagnostics_or_empty = std::collections::HashSet::new(); let mut needs_moved = std::collections::HashSet::new();
// Generates calls to `span_label` and similar functions based on the attributes // Generates calls to `span_label` and similar functions based on the attributes
// on fields. Code for suggestions uses formatting machinery and the value of // on fields. Code for suggestions uses formatting machinery and the value of
@ -92,16 +93,11 @@ impl DiagnosticDeriveBuilder {
let attrs = structure let attrs = structure
.clone() .clone()
.filter(|field_binding| { .filter(|field_binding| {
let attrs = &field_binding.ast().attrs; let ast = &field_binding.ast();
!self.needs_move(ast) || {
(!attrs.is_empty() needs_moved.insert(field_binding.binding.clone());
&& attrs.iter().all(|attr| { false
"subdiagnostic" != attr.path.segments.last().unwrap().ident.to_string() }
}))
|| {
subdiagnostics_or_empty.insert(field_binding.binding.clone());
false
}
}) })
.each(|field_binding| self.generate_field_attrs_code(field_binding)); .each(|field_binding| self.generate_field_attrs_code(field_binding));
@ -111,12 +107,41 @@ impl DiagnosticDeriveBuilder {
// attributes or a `#[subdiagnostic]` attribute then it must be passed as an // attributes or a `#[subdiagnostic]` attribute then it must be passed as an
// argument to the diagnostic so that it can be referred to by Fluent messages. // argument to the diagnostic so that it can be referred to by Fluent messages.
let args = structure let args = structure
.filter(|field_binding| subdiagnostics_or_empty.contains(&field_binding.binding)) .filter(|field_binding| needs_moved.contains(&field_binding.binding))
.each(|field_binding| self.generate_field_attrs_code(field_binding)); .each(|field_binding| self.generate_field_attrs_code(field_binding));
(attrs, args) (attrs, args)
} }
/// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic
/// call (like `span_label`).
fn should_generate_set_arg(&self, field: &Field) -> bool {
field.attrs.is_empty()
}
/// Returns `true` if `field` needs to have code generated in the by-move branch of the
/// generated derive rather than the by-ref branch.
fn needs_move(&self, field: &Field) -> bool {
let generates_set_arg = self.should_generate_set_arg(field);
let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]);
// FIXME(davidtwco): better support for one field needing to be in the by-move and
// by-ref branches.
let is_subdiagnostic = field
.attrs
.iter()
.map(|attr| attr.path.segments.last().unwrap().ident.to_string())
.any(|attr| attr == "subdiagnostic");
// `set_arg` calls take their argument by-move..
generates_set_arg
// If this is a `MultiSpan` field then it needs to be moved to be used by any
// attribute..
|| is_multispan
// If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is
// unlikely to be `Copy`..
|| is_subdiagnostic
}
/// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
/// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates /// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates
/// diagnostic builder calls for setting error code and creating note/help messages. /// diagnostic builder calls for setting error code and creating note/help messages.
@ -131,7 +156,7 @@ impl DiagnosticDeriveBuilder {
let name = name.as_str(); let name = name.as_str();
let meta = attr.parse_meta()?; let meta = attr.parse_meta()?;
let is_help_or_note = matches!(name, "help" | "note"); let is_help_note_or_warn = matches!(name, "help" | "note" | "warn_");
let nested = match meta { let nested = match meta {
// Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or // Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or
@ -139,8 +164,12 @@ impl DiagnosticDeriveBuilder {
Meta::List(MetaList { ref nested, .. }) => nested, Meta::List(MetaList { ref nested, .. }) => nested,
// Subdiagnostics without spans can be applied to the type too, and these are just // Subdiagnostics without spans can be applied to the type too, and these are just
// paths: `#[help]` and `#[note]` // paths: `#[help]` and `#[note]`
Meta::Path(_) if is_help_or_note => { Meta::Path(_) if is_help_note_or_warn => {
let fn_name = proc_macro2::Ident::new(name, attr.span()); let fn_name = if name == "warn_" {
Ident::new("warn", attr.span())
} else {
Ident::new(name, attr.span())
};
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); }); return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); });
} }
_ => throw_invalid_attr!(attr, &meta), _ => throw_invalid_attr!(attr, &meta),
@ -152,9 +181,11 @@ impl DiagnosticDeriveBuilder {
"error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)), "error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)),
"warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)), "warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)),
"lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)), "lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)),
"help" | "note" => (), "help" | "note" | "warn_" => (),
_ => throw_invalid_attr!(attr, &meta, |diag| { _ => throw_invalid_attr!(attr, &meta, |diag| {
diag.help("only `error`, `warning`, `help` and `note` are valid attributes") diag.help(
"only `error`, `warning`, `help`, `note` and `warn_` are valid attributes",
)
}), }),
} }
@ -163,14 +194,16 @@ impl DiagnosticDeriveBuilder {
let mut nested_iter = nested.into_iter(); let mut nested_iter = nested.into_iter();
if let Some(nested_attr) = nested_iter.next() { if let Some(nested_attr) = nested_iter.next() {
// Report an error if there are any other list items after the path. // Report an error if there are any other list items after the path.
if is_help_or_note && nested_iter.next().is_some() { if is_help_note_or_warn && nested_iter.next().is_some() {
throw_invalid_nested_attr!(attr, &nested_attr, |diag| { throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
diag.help("`help` and `note` struct attributes can only have one argument") diag.help(
"`help`, `note` and `warn_` struct attributes can only have one argument",
)
}); });
} }
match nested_attr { match nested_attr {
NestedMeta::Meta(Meta::Path(path)) if is_help_or_note => { NestedMeta::Meta(Meta::Path(path)) if is_help_note_or_warn => {
let fn_name = proc_macro2::Ident::new(name, attr.span()); let fn_name = proc_macro2::Ident::new(name, attr.span());
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); }); return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
} }
@ -178,7 +211,7 @@ impl DiagnosticDeriveBuilder {
self.slug.set_once((path.clone(), span)); self.slug.set_once((path.clone(), span));
} }
NestedMeta::Meta(meta @ Meta::NameValue(_)) NestedMeta::Meta(meta @ Meta::NameValue(_))
if !is_help_or_note if !is_help_note_or_warn
&& meta.path().segments.last().unwrap().ident.to_string() == "code" => && meta.path().segments.last().unwrap().ident.to_string() == "code" =>
{ {
// don't error for valid follow-up attributes // don't error for valid follow-up attributes
@ -227,57 +260,55 @@ impl DiagnosticDeriveBuilder {
let field = binding_info.ast(); let field = binding_info.ast();
let field_binding = &binding_info.binding; let field_binding = &binding_info.binding;
let inner_ty = FieldInnerTy::from_type(&field.ty); if self.should_generate_set_arg(&field) {
// When generating `set_arg` or `add_subdiagnostic` calls, move data rather than
// borrow it to avoid requiring clones - this must therefore be the last use of
// each field (for example, any formatting machinery that might refer to a field
// should be generated already).
if field.attrs.is_empty() {
let diag = &self.diag; let diag = &self.diag;
let ident = field.ident.as_ref().unwrap(); let ident = field.ident.as_ref().unwrap();
quote! { return quote! {
#diag.set_arg( #diag.set_arg(
stringify!(#ident), stringify!(#ident),
#field_binding #field_binding
); );
} };
} else {
field
.attrs
.iter()
.map(move |attr| {
let name = attr.path.segments.last().unwrap().ident.to_string();
let (binding, needs_destructure) = match (name.as_str(), &inner_ty) {
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
("primary_span", FieldInnerTy::Vec(_)) => {
(quote! { #field_binding.clone() }, false)
}
// `subdiagnostics` are not derefed because they are bound by value.
("subdiagnostic", _) => (quote! { #field_binding }, true),
_ => (quote! { *#field_binding }, true),
};
let generated_code = self
.generate_inner_field_code(
attr,
FieldInfo {
binding: binding_info,
ty: inner_ty.inner_type().unwrap_or(&field.ty),
span: &field.span(),
},
binding,
)
.unwrap_or_else(|v| v.to_compile_error());
if needs_destructure {
inner_ty.with(field_binding, generated_code)
} else {
generated_code
}
})
.collect()
} }
let needs_move = self.needs_move(&field);
let inner_ty = FieldInnerTy::from_type(&field.ty);
field
.attrs
.iter()
.map(move |attr| {
let name = attr.path.segments.last().unwrap().ident.to_string();
let needs_clone =
name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_));
let (binding, needs_destructure) = if needs_clone {
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
(quote! { #field_binding.clone() }, false)
} else if needs_move {
(quote! { #field_binding }, true)
} else {
(quote! { *#field_binding }, true)
};
let generated_code = self
.generate_inner_field_code(
attr,
FieldInfo {
binding: binding_info,
ty: inner_ty.inner_type().unwrap_or(&field.ty),
span: &field.span(),
},
binding,
)
.unwrap_or_else(|v| v.to_compile_error());
if needs_destructure {
inner_ty.with(field_binding, generated_code)
} else {
generated_code
}
})
.collect()
} }
fn generate_inner_field_code( fn generate_inner_field_code(
@ -324,10 +355,12 @@ impl DiagnosticDeriveBuilder {
report_error_if_not_applied_to_span(attr, &info)?; report_error_if_not_applied_to_span(attr, &info)?;
Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label })) Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label }))
} }
"note" | "help" => { "note" | "help" | "warn_" => {
let path = match name { let warn_ident = Ident::new("warn", Span::call_site());
"note" => parse_quote! { _subdiag::note }, let (ident, path) = match name {
"help" => parse_quote! { _subdiag::help }, "note" => (ident, parse_quote! { _subdiag::note }),
"help" => (ident, parse_quote! { _subdiag::help }),
"warn_" => (&warn_ident, parse_quote! { _subdiag::warn }),
_ => unreachable!(), _ => unreachable!(),
}; };
if type_matches_path(&info.ty, &["rustc_span", "Span"]) { if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
@ -364,10 +397,10 @@ impl DiagnosticDeriveBuilder {
"suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => { "suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => {
return self.generate_inner_field_code_suggestion(attr, info); return self.generate_inner_field_code_suggestion(attr, info);
} }
"label" | "help" | "note" => (), "label" | "help" | "note" | "warn_" => (),
_ => throw_invalid_attr!(attr, &meta, |diag| { _ => throw_invalid_attr!(attr, &meta, |diag| {
diag.help( diag.help(
"only `label`, `note`, `help` or `suggestion{,_short,_hidden,_verbose}` are \ "only `label`, `help`, `note`, `warn` or `suggestion{,_short,_hidden,_verbose}` are \
valid field attributes", valid field attributes",
) )
}), }),
@ -396,7 +429,14 @@ impl DiagnosticDeriveBuilder {
Ok(self.add_spanned_subdiagnostic(binding, ident, msg)) Ok(self.add_spanned_subdiagnostic(binding, ident, msg))
} }
"note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)), "note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)),
"note" | "help" => report_type_error(attr, "`Span` or `()`")?, // `warn_` must be special-cased because the attribute `warn` already has meaning and
// so isn't used, despite the diagnostic API being named `warn`.
"warn_" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => Ok(self
.add_spanned_subdiagnostic(binding, &Ident::new("warn", Span::call_site()), msg)),
"warn_" if type_is_unit(&info.ty) => {
Ok(self.add_subdiagnostic(&Ident::new("warn", Span::call_site()), msg))
}
"note" | "help" | "warn_" => report_type_error(attr, "`Span` or `()`")?,
_ => unreachable!(), _ => unreachable!(),
} }
} }

View file

@ -260,10 +260,12 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
#generated #generated
pub mod _subdiag { pub mod _subdiag {
pub const note: crate::SubdiagnosticMessage =
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
pub const help: crate::SubdiagnosticMessage = pub const help: crate::SubdiagnosticMessage =
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help")); crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help"));
pub const note: crate::SubdiagnosticMessage =
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
pub const warn: crate::SubdiagnosticMessage =
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("warn"));
pub const label: crate::SubdiagnosticMessage = pub const label: crate::SubdiagnosticMessage =
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label")); crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label"));
pub const suggestion: crate::SubdiagnosticMessage = pub const suggestion: crate::SubdiagnosticMessage =

View file

@ -37,6 +37,8 @@ enum SubdiagnosticKind {
Note, Note,
/// `#[help(...)]` /// `#[help(...)]`
Help, Help,
/// `#[warn_(...)]`
Warn,
/// `#[suggestion{,_short,_hidden,_verbose}]` /// `#[suggestion{,_short,_hidden,_verbose}]`
Suggestion(SubdiagnosticSuggestionKind), Suggestion(SubdiagnosticSuggestionKind),
} }
@ -49,6 +51,7 @@ impl FromStr for SubdiagnosticKind {
"label" => Ok(SubdiagnosticKind::Label), "label" => Ok(SubdiagnosticKind::Label),
"note" => Ok(SubdiagnosticKind::Note), "note" => Ok(SubdiagnosticKind::Note),
"help" => Ok(SubdiagnosticKind::Help), "help" => Ok(SubdiagnosticKind::Help),
"warn_" => Ok(SubdiagnosticKind::Warn),
"suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)), "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
"suggestion_short" => { "suggestion_short" => {
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short)) Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
@ -70,6 +73,7 @@ impl quote::IdentFragment for SubdiagnosticKind {
SubdiagnosticKind::Label => write!(f, "label"), SubdiagnosticKind::Label => write!(f, "label"),
SubdiagnosticKind::Note => write!(f, "note"), SubdiagnosticKind::Note => write!(f, "note"),
SubdiagnosticKind::Help => write!(f, "help"), SubdiagnosticKind::Help => write!(f, "help"),
SubdiagnosticKind::Warn => write!(f, "warn"),
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => { SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
write!(f, "suggestion") write!(f, "suggestion")
} }

View file

@ -85,7 +85,13 @@ pub(crate) fn report_error_if_not_applied_to_span(
attr: &Attribute, attr: &Attribute,
info: &FieldInfo<'_>, info: &FieldInfo<'_>,
) -> Result<(), DiagnosticDeriveError> { ) -> Result<(), DiagnosticDeriveError> {
report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`") if !type_matches_path(&info.ty, &["rustc_span", "Span"])
&& !type_matches_path(&info.ty, &["rustc_errors", "MultiSpan"])
{
report_type_error(attr, "`Span` or `MultiSpan`")?;
}
Ok(())
} }
/// Inner type of a field and type of wrapper. /// Inner type of a field and type of wrapper.

View file

@ -130,8 +130,9 @@ decl_derive!(
warning, warning,
error, error,
lint, lint,
note,
help, help,
note,
warn_,
// field attributes // field attributes
skip_arg, skip_arg,
primary_span, primary_span,
@ -148,8 +149,9 @@ decl_derive!(
warning, warning,
error, error,
lint, lint,
note,
help, help,
note,
warn_,
// field attributes // field attributes
skip_arg, skip_arg,
primary_span, primary_span,
@ -166,6 +168,7 @@ decl_derive!(
label, label,
help, help,
note, note,
warn_,
suggestion, suggestion,
suggestion_short, suggestion_short,
suggestion_hidden, suggestion_hidden,

View file

@ -1397,6 +1397,7 @@ impl<V, T> ProjectionElem<V, T> {
Self::Field(_, _) Self::Field(_, _)
| Self::Index(_) | Self::Index(_)
| Self::OpaqueCast(_)
| Self::ConstantIndex { .. } | Self::ConstantIndex { .. }
| Self::Subslice { .. } | Self::Subslice { .. }
| Self::Downcast(_, _) => false, | Self::Downcast(_, _) => false,
@ -1574,7 +1575,9 @@ impl Debug for Place<'_> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
for elem in self.projection.iter().rev() { for elem in self.projection.iter().rev() {
match elem { match elem {
ProjectionElem::Downcast(_, _) | ProjectionElem::Field(_, _) => { ProjectionElem::OpaqueCast(_)
| ProjectionElem::Downcast(_, _)
| ProjectionElem::Field(_, _) => {
write!(fmt, "(").unwrap(); write!(fmt, "(").unwrap();
} }
ProjectionElem::Deref => { ProjectionElem::Deref => {
@ -1590,6 +1593,9 @@ impl Debug for Place<'_> {
for elem in self.projection.iter() { for elem in self.projection.iter() {
match elem { match elem {
ProjectionElem::OpaqueCast(ty) => {
write!(fmt, " as {})", ty)?;
}
ProjectionElem::Downcast(Some(name), _index) => { ProjectionElem::Downcast(Some(name), _index) => {
write!(fmt, " as {})", name)?; write!(fmt, " as {})", name)?;
} }

View file

@ -754,6 +754,9 @@ pub type AssertMessage<'tcx> = AssertKind<Operand<'tcx>>;
/// generator has more than one variant, the parent place's variant index must be set, indicating /// generator has more than one variant, the parent place's variant index must be set, indicating
/// which variant is being used. If it has just one variant, the variant index may or may not be /// which variant is being used. If it has just one variant, the variant index may or may not be
/// included - the single possible variant is inferred if it is not included. /// included - the single possible variant is inferred if it is not included.
/// - [`OpaqueCast`](ProjectionElem::OpaqueCast): This projection changes the place's type to the
/// given one, and makes no other changes. A `OpaqueCast` projection on any type other than an
/// opaque type from the current crate is not well-formed.
/// - [`ConstantIndex`](ProjectionElem::ConstantIndex): Computes an offset in units of `T` into the /// - [`ConstantIndex`](ProjectionElem::ConstantIndex): Computes an offset in units of `T` into the
/// place as described in the documentation for the `ProjectionElem`. The resulting address is /// place as described in the documentation for the `ProjectionElem`. The resulting address is
/// the parent's address plus that offset, and the type is `T`. This is only legal if the parent /// the parent's address plus that offset, and the type is `T`. This is only legal if the parent
@ -856,6 +859,10 @@ pub enum ProjectionElem<V, T> {
/// ///
/// The included Symbol is the name of the variant, used for printing MIR. /// The included Symbol is the name of the variant, used for printing MIR.
Downcast(Option<Symbol>, VariantIdx), Downcast(Option<Symbol>, VariantIdx),
/// Like an explicit cast from an opaque type to a concrete type, but without
/// requiring an intermediate variable.
OpaqueCast(T),
} }
/// Alias for projections as they appear in places, where the base is a place /// Alias for projections as they appear in places, where the base is a place

View file

@ -57,7 +57,7 @@ impl<'tcx> PlaceTy<'tcx> {
/// `PlaceElem`, where we can just use the `Ty` that is already /// `PlaceElem`, where we can just use the `Ty` that is already
/// stored inline on field projection elems. /// stored inline on field projection elems.
pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> { pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> {
self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty) self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty, |_, ty| ty)
} }
/// `place_ty.projection_ty_core(tcx, elem, |...| { ... })` /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
@ -71,6 +71,7 @@ impl<'tcx> PlaceTy<'tcx> {
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
elem: &ProjectionElem<V, T>, elem: &ProjectionElem<V, T>,
mut handle_field: impl FnMut(&Self, Field, T) -> Ty<'tcx>, mut handle_field: impl FnMut(&Self, Field, T) -> Ty<'tcx>,
mut handle_opaque_cast: impl FnMut(&Self, T) -> Ty<'tcx>,
) -> PlaceTy<'tcx> ) -> PlaceTy<'tcx>
where where
V: ::std::fmt::Debug, V: ::std::fmt::Debug,
@ -109,6 +110,7 @@ impl<'tcx> PlaceTy<'tcx> {
PlaceTy { ty: self.ty, variant_index: Some(index) } PlaceTy { ty: self.ty, variant_index: Some(index) }
} }
ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)), ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast(&self, ty)),
}; };
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer); debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
answer answer

View file

@ -182,6 +182,7 @@ impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> {
Ok(match self { Ok(match self {
Deref => Deref, Deref => Deref,
Field(f, ty) => Field(f, ty.try_fold_with(folder)?), Field(f, ty) => Field(f, ty.try_fold_with(folder)?),
OpaqueCast(ty) => OpaqueCast(ty.try_fold_with(folder)?),
Index(v) => Index(v.try_fold_with(folder)?), Index(v) => Index(v.try_fold_with(folder)?),
Downcast(symbol, variantidx) => Downcast(symbol, variantidx), Downcast(symbol, variantidx) => Downcast(symbol, variantidx),
ConstantIndex { offset, min_length, from_end } => { ConstantIndex { offset, min_length, from_end } => {

View file

@ -1064,6 +1064,11 @@ macro_rules! visit_place_fns {
self.visit_ty(&mut new_ty, TyContext::Location(location)); self.visit_ty(&mut new_ty, TyContext::Location(location));
if ty != new_ty { Some(PlaceElem::Field(field, new_ty)) } else { None } if ty != new_ty { Some(PlaceElem::Field(field, new_ty)) } else { None }
} }
PlaceElem::OpaqueCast(ty) => {
let mut new_ty = ty;
self.visit_ty(&mut new_ty, TyContext::Location(location));
if ty != new_ty { Some(PlaceElem::OpaqueCast(new_ty)) } else { None }
}
PlaceElem::Deref PlaceElem::Deref
| PlaceElem::ConstantIndex { .. } | PlaceElem::ConstantIndex { .. }
| PlaceElem::Subslice { .. } | PlaceElem::Subslice { .. }
@ -1133,7 +1138,7 @@ macro_rules! visit_place_fns {
location: Location, location: Location,
) { ) {
match elem { match elem {
ProjectionElem::Field(_field, ty) => { ProjectionElem::OpaqueCast(ty) | ProjectionElem::Field(_, ty) => {
self.visit_ty(ty, TyContext::Location(location)); self.visit_ty(ty, TyContext::Location(location));
} }
ProjectionElem::Index(local) => { ProjectionElem::Index(local) => {

View file

@ -542,17 +542,9 @@ pub enum SelectionError<'tcx> {
ErrorReporting, ErrorReporting,
/// Multiple applicable `impl`s where found. The `DefId`s correspond to /// Multiple applicable `impl`s where found. The `DefId`s correspond to
/// all the `impl`s' Items. /// all the `impl`s' Items.
Ambiguous(Vec<AmbiguousSelection>), Ambiguous(Vec<DefId>),
} }
#[derive(Copy, Clone, Debug)]
pub enum AmbiguousSelection {
Impl(DefId),
ParamEnv(Span),
}
TrivialTypeTraversalAndLiftImpls! { AmbiguousSelection, }
/// When performing resolution, it is typically the case that there /// When performing resolution, it is typically the case that there
/// can be one of three outcomes: /// can be one of three outcomes:
/// ///

View file

@ -1975,7 +1975,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
min_size = field_end; min_size = field_end;
} }
FieldInfo { FieldInfo {
name: name.to_string(), name,
offset: offset.bytes(), offset: offset.bytes(),
size: field_layout.size.bytes(), size: field_layout.size.bytes(),
align: field_layout.align.abi.bytes(), align: field_layout.align.abi.bytes(),
@ -1984,7 +1984,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
.collect(); .collect();
VariantInfo { VariantInfo {
name: n.map(|n| n.to_string()), name: n,
kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact },
align: layout.align.abi.bytes(), align: layout.align.abi.bytes(),
size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() }, size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() },

View file

@ -1030,11 +1030,11 @@ pub trait PrettyPrinter<'tcx>:
} }
} }
fn ty_infer_name(&self, _: ty::TyVid) -> Option<String> { fn ty_infer_name(&self, _: ty::TyVid) -> Option<Symbol> {
None None
} }
fn const_infer_name(&self, _: ty::ConstVid<'tcx>) -> Option<String> { fn const_infer_name(&self, _: ty::ConstVid<'tcx>) -> Option<Symbol> {
None None
} }
@ -1550,8 +1550,8 @@ pub struct FmtPrinterData<'a, 'tcx> {
pub region_highlight_mode: RegionHighlightMode<'tcx>, pub region_highlight_mode: RegionHighlightMode<'tcx>,
pub ty_infer_name_resolver: Option<Box<dyn Fn(ty::TyVid) -> Option<String> + 'a>>, pub ty_infer_name_resolver: Option<Box<dyn Fn(ty::TyVid) -> Option<Symbol> + 'a>>,
pub const_infer_name_resolver: Option<Box<dyn Fn(ty::ConstVid<'tcx>) -> Option<String> + 'a>>, pub const_infer_name_resolver: Option<Box<dyn Fn(ty::ConstVid<'tcx>) -> Option<Symbol> + 'a>>,
} }
impl<'a, 'tcx> Deref for FmtPrinter<'a, 'tcx> { impl<'a, 'tcx> Deref for FmtPrinter<'a, 'tcx> {
@ -1841,11 +1841,11 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
} }
impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
fn ty_infer_name(&self, id: ty::TyVid) -> Option<String> { fn ty_infer_name(&self, id: ty::TyVid) -> Option<Symbol> {
self.0.ty_infer_name_resolver.as_ref().and_then(|func| func(id)) self.0.ty_infer_name_resolver.as_ref().and_then(|func| func(id))
} }
fn const_infer_name(&self, id: ty::ConstVid<'tcx>) -> Option<String> { fn const_infer_name(&self, id: ty::ConstVid<'tcx>) -> Option<Symbol> {
self.0.const_infer_name_resolver.as_ref().and_then(|func| func(id)) self.0.const_infer_name_resolver.as_ref().and_then(|func| func(id))
} }

View file

@ -7,6 +7,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::hir::place::Projection as HirProjection; use rustc_middle::hir::place::Projection as HirProjection;
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind; use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
use rustc_middle::middle::region; use rustc_middle::middle::region;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::AssertKind::BoundsCheck; use rustc_middle::mir::AssertKind::BoundsCheck;
use rustc_middle::mir::*; use rustc_middle::mir::*;
use rustc_middle::thir::*; use rustc_middle::thir::*;
@ -71,7 +72,7 @@ pub(crate) enum PlaceBase {
/// This is used internally when building a place for an expression like `a.b.c`. The fields `b` /// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
/// and `c` can be progressively pushed onto the place builder that is created when converting `a`. /// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub(crate) struct PlaceBuilder<'tcx> { pub(in crate::build) struct PlaceBuilder<'tcx> {
base: PlaceBase, base: PlaceBase,
projection: Vec<PlaceElem<'tcx>>, projection: Vec<PlaceElem<'tcx>>,
} }
@ -104,6 +105,8 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
variant = Some(*idx); variant = Some(*idx);
continue; continue;
} }
// These do not affect anything, they just make sure we know the right type.
ProjectionElem::OpaqueCast(_) => continue,
ProjectionElem::Index(..) ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. } | ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => { | ProjectionElem::Subslice { .. } => {
@ -201,10 +204,10 @@ fn find_capture_matching_projections<'a, 'tcx>(
/// `PlaceBuilder` now starts from `PlaceBase::Local`. /// `PlaceBuilder` now starts from `PlaceBase::Local`.
/// ///
/// Returns a Result with the error being the PlaceBuilder (`from_builder`) that was not found. /// Returns a Result with the error being the PlaceBuilder (`from_builder`) that was not found.
fn to_upvars_resolved_place_builder<'a, 'tcx>( #[instrument(level = "trace", skip(cx))]
fn to_upvars_resolved_place_builder<'tcx>(
from_builder: PlaceBuilder<'tcx>, from_builder: PlaceBuilder<'tcx>,
tcx: TyCtxt<'tcx>, cx: &Builder<'_, 'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> { ) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
match from_builder.base { match from_builder.base {
PlaceBase::Local(_) => Ok(from_builder), PlaceBase::Local(_) => Ok(from_builder),
@ -219,13 +222,13 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
let Some((capture_index, capture)) = let Some((capture_index, capture)) =
find_capture_matching_projections( find_capture_matching_projections(
typeck_results, cx.typeck_results,
var_hir_id, var_hir_id,
closure_def_id, closure_def_id,
&from_builder.projection, &from_builder.projection,
) else { ) else {
let closure_span = tcx.def_span(closure_def_id); let closure_span = cx.tcx.def_span(closure_def_id);
if !enable_precise_capture(tcx, closure_span) { if !enable_precise_capture(cx.tcx, closure_span) {
bug!( bug!(
"No associated capture found for {:?}[{:#?}] even though \ "No associated capture found for {:?}[{:#?}] even though \
capture_disjoint_fields isn't enabled", capture_disjoint_fields isn't enabled",
@ -242,8 +245,8 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
}; };
// We won't be building MIR if the closure wasn't local // We won't be building MIR if the closure wasn't local
let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); let closure_hir_id = cx.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local());
let closure_ty = typeck_results.node_type(closure_hir_id); let closure_ty = cx.typeck_results.node_type(closure_hir_id);
let substs = match closure_ty.kind() { let substs = match closure_ty.kind() {
ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
@ -270,12 +273,14 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
// We used some of the projections to build the capture itself, // We used some of the projections to build the capture itself,
// now we apply the remaining to the upvar resolved place. // now we apply the remaining to the upvar resolved place.
trace!(?capture.place, ?from_builder.projection);
let remaining_projections = strip_prefix( let remaining_projections = strip_prefix(
capture.place.base_ty, capture.place.base_ty,
from_builder.projection, from_builder.projection,
&capture.place.projections, &capture.place.projections,
); );
upvar_resolved_place_builder.projection.extend(remaining_projections); upvar_resolved_place_builder.projection.extend(remaining_projections);
trace!(?upvar_resolved_place_builder);
Ok(upvar_resolved_place_builder) Ok(upvar_resolved_place_builder)
} }
@ -294,16 +299,21 @@ fn strip_prefix<'tcx>(
prefix_projections: &[HirProjection<'tcx>], prefix_projections: &[HirProjection<'tcx>],
) -> impl Iterator<Item = PlaceElem<'tcx>> { ) -> impl Iterator<Item = PlaceElem<'tcx>> {
let mut iter = projections.into_iter(); let mut iter = projections.into_iter();
let mut next = || match iter.next()? {
// Filter out opaque casts, they are unnecessary in the prefix.
ProjectionElem::OpaqueCast(..) => iter.next(),
other => Some(other),
};
for projection in prefix_projections { for projection in prefix_projections {
match projection.kind { match projection.kind {
HirProjectionKind::Deref => { HirProjectionKind::Deref => {
assert!(matches!(iter.next(), Some(ProjectionElem::Deref))); assert!(matches!(next(), Some(ProjectionElem::Deref)));
} }
HirProjectionKind::Field(..) => { HirProjectionKind::Field(..) => {
if base_ty.is_enum() { if base_ty.is_enum() {
assert!(matches!(iter.next(), Some(ProjectionElem::Downcast(..)))); assert!(matches!(next(), Some(ProjectionElem::Downcast(..))));
} }
assert!(matches!(iter.next(), Some(ProjectionElem::Field(..)))); assert!(matches!(next(), Some(ProjectionElem::Field(..))));
} }
HirProjectionKind::Index | HirProjectionKind::Subslice => { HirProjectionKind::Index | HirProjectionKind::Subslice => {
bug!("unexpected projection kind: {:?}", projection); bug!("unexpected projection kind: {:?}", projection);
@ -315,24 +325,32 @@ fn strip_prefix<'tcx>(
} }
impl<'tcx> PlaceBuilder<'tcx> { impl<'tcx> PlaceBuilder<'tcx> {
pub(crate) fn into_place<'a>( pub(crate) fn into_place(self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> {
self,
tcx: TyCtxt<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
) -> Place<'tcx> {
if let PlaceBase::Local(local) = self.base { if let PlaceBase::Local(local) = self.base {
Place { local, projection: tcx.intern_place_elems(&self.projection) } let mut projections = vec![];
let mut ty = PlaceTy::from_ty(cx.local_decls[local].ty);
for projection in self.projection {
// Only preserve those opaque casts that actually go from an opaque type
// to another type.
if let ProjectionElem::OpaqueCast(t) = projection {
if let ty::Opaque(..) = ty.ty.kind() {
if t != ty.ty {
projections.push(ProjectionElem::OpaqueCast(t));
}
}
} else {
projections.push(projection);
}
ty = ty.projection_ty(cx.tcx, projection);
}
Place { local, projection: cx.tcx.intern_place_elems(&projections) }
} else { } else {
self.expect_upvars_resolved(tcx, typeck_results).into_place(tcx, typeck_results) self.expect_upvars_resolved(cx).into_place(cx)
} }
} }
fn expect_upvars_resolved<'a>( fn expect_upvars_resolved(self, cx: &Builder<'_, 'tcx>) -> PlaceBuilder<'tcx> {
self, to_upvars_resolved_place_builder(self, cx).unwrap()
tcx: TyCtxt<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
) -> PlaceBuilder<'tcx> {
to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap()
} }
/// Attempts to resolve the `PlaceBuilder`. /// Attempts to resolve the `PlaceBuilder`.
@ -346,12 +364,11 @@ impl<'tcx> PlaceBuilder<'tcx> {
/// not captured. This can happen because the final mir that will be /// not captured. This can happen because the final mir that will be
/// generated doesn't require a read for this place. Failures will only /// generated doesn't require a read for this place. Failures will only
/// happen inside closures. /// happen inside closures.
pub(crate) fn try_upvars_resolved<'a>( pub(crate) fn try_upvars_resolved(
self, self,
tcx: TyCtxt<'tcx>, cx: &Builder<'_, 'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> { ) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
to_upvars_resolved_place_builder(self, tcx, typeck_results) to_upvars_resolved_place_builder(self, cx)
} }
pub(crate) fn base(&self) -> PlaceBase { pub(crate) fn base(&self) -> PlaceBase {
@ -411,7 +428,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
expr: &Expr<'tcx>, expr: &Expr<'tcx>,
) -> BlockAnd<Place<'tcx>> { ) -> BlockAnd<Place<'tcx>> {
let place_builder = unpack!(block = self.as_place_builder(block, expr)); let place_builder = unpack!(block = self.as_place_builder(block, expr));
block.and(place_builder.into_place(self.tcx, self.typeck_results)) block.and(place_builder.into_place(self))
} }
/// This is used when constructing a compound `Place`, so that we can avoid creating /// This is used when constructing a compound `Place`, so that we can avoid creating
@ -435,7 +452,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
expr: &Expr<'tcx>, expr: &Expr<'tcx>,
) -> BlockAnd<Place<'tcx>> { ) -> BlockAnd<Place<'tcx>> {
let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr)); let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
block.and(place_builder.into_place(self.tcx, self.typeck_results)) block.and(place_builder.into_place(self))
} }
/// This is used when constructing a compound `Place`, so that we can avoid creating /// This is used when constructing a compound `Place`, so that we can avoid creating
@ -530,7 +547,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
inferred_ty: expr.ty, inferred_ty: expr.ty,
}); });
let place = place_builder.clone().into_place(this.tcx, this.typeck_results); let place = place_builder.clone().into_place(this);
this.cfg.push( this.cfg.push(
block, block,
Statement { Statement {
@ -682,7 +699,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if is_outermost_index { if is_outermost_index {
self.read_fake_borrows(block, fake_borrow_temps, source_info) self.read_fake_borrows(block, fake_borrow_temps, source_info)
} else { } else {
base_place = base_place.expect_upvars_resolved(self.tcx, self.typeck_results); base_place = base_place.expect_upvars_resolved(self);
self.add_fake_borrows_of_base( self.add_fake_borrows_of_base(
&base_place, &base_place,
block, block,
@ -710,12 +727,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let lt = self.temp(bool_ty, expr_span); let lt = self.temp(bool_ty, expr_span);
// len = len(slice) // len = len(slice)
self.cfg.push_assign( self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.into_place(self)));
block,
source_info,
len,
Rvalue::Len(slice.into_place(self.tcx, self.typeck_results)),
);
// lt = idx < len // lt = idx < len
self.cfg.push_assign( self.cfg.push_assign(
block, block,
@ -795,6 +807,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
ProjectionElem::Field(..) ProjectionElem::Field(..)
| ProjectionElem::Downcast(..) | ProjectionElem::Downcast(..)
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::ConstantIndex { .. } | ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => (), | ProjectionElem::Subslice { .. } => (),
} }

View file

@ -321,11 +321,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let place_builder = let place_builder =
unpack!(block = this.as_place_builder(block, &this.thir[*thir_place])); unpack!(block = this.as_place_builder(block, &this.thir[*thir_place]));
if let Ok(place_builder_resolved) = if let Ok(place_builder_resolved) = place_builder.try_upvars_resolved(this) {
place_builder.try_upvars_resolved(this.tcx, this.typeck_results) let mir_place = place_builder_resolved.into_place(this);
{
let mir_place =
place_builder_resolved.into_place(this.tcx, this.typeck_results);
this.cfg.push_fake_read( this.cfg.push_fake_read(
block, block,
this.source_info(this.tcx.hir().span(*hir_id)), this.source_info(this.tcx.hir().span(*hir_id)),
@ -616,8 +613,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// by the parent itself. The mutability of the current capture // by the parent itself. The mutability of the current capture
// is same as that of the capture in the parent closure. // is same as that of the capture in the parent closure.
PlaceBase::Upvar { .. } => { PlaceBase::Upvar { .. } => {
let enclosing_upvars_resolved = let enclosing_upvars_resolved = arg_place_builder.clone().into_place(this);
arg_place_builder.clone().into_place(this.tcx, this.typeck_results);
match enclosing_upvars_resolved.as_ref() { match enclosing_upvars_resolved.as_ref() {
PlaceRef { PlaceRef {
@ -654,7 +650,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false }, Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
}; };
let arg_place = arg_place_builder.into_place(this.tcx, this.typeck_results); let arg_place = arg_place_builder.into_place(this);
this.cfg.push_assign( this.cfg.push_assign(
block, block,

View file

@ -23,6 +23,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr, mutability)) ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr, mutability))
} }
#[instrument(skip(self), level = "debug")]
fn as_temp_inner( fn as_temp_inner(
&mut self, &mut self,
mut block: BasicBlock, mut block: BasicBlock,
@ -30,10 +31,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
expr: &Expr<'tcx>, expr: &Expr<'tcx>,
mutability: Mutability, mutability: Mutability,
) -> BlockAnd<Local> { ) -> BlockAnd<Local> {
debug!(
"as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
block, temp_lifetime, expr, mutability
);
let this = self; let this = self;
let expr_span = expr.span; let expr_span = expr.span;

View file

@ -15,14 +15,13 @@ use std::iter;
impl<'a, 'tcx> Builder<'a, 'tcx> { impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Compile `expr`, storing the result into `destination`, which /// Compile `expr`, storing the result into `destination`, which
/// is assumed to be uninitialized. /// is assumed to be uninitialized.
#[instrument(level = "debug", skip(self))]
pub(crate) fn expr_into_dest( pub(crate) fn expr_into_dest(
&mut self, &mut self,
destination: Place<'tcx>, destination: Place<'tcx>,
mut block: BasicBlock, mut block: BasicBlock,
expr: &Expr<'tcx>, expr: &Expr<'tcx>,
) -> BlockAnd<()> { ) -> BlockAnd<()> {
debug!("expr_into_dest(destination={:?}, block={:?}, expr={:?})", destination, block, expr);
// since we frequently have to reference `self` from within a // since we frequently have to reference `self` from within a
// closure, where `self` would be shadowed, it's easier to // closure, where `self` would be shadowed, it's easier to
// just use the name `this` uniformly // just use the name `this` uniformly
@ -366,9 +365,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
None => { None => {
let place_builder = place_builder.clone(); let place_builder = place_builder.clone();
this.consume_by_copy_or_move( this.consume_by_copy_or_move(
place_builder place_builder.field(n, *ty).into_place(this),
.field(n, *ty)
.into_place(this.tcx, this.typeck_results),
) )
} }
}) })

View file

@ -220,10 +220,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let cause_matched_place = FakeReadCause::ForMatchedPlace(None); let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
let source_info = self.source_info(scrutinee_span); let source_info = self.source_info(scrutinee_span);
if let Ok(scrutinee_builder) = if let Ok(scrutinee_builder) = scrutinee_place_builder.clone().try_upvars_resolved(self) {
scrutinee_place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results) let scrutinee_place = scrutinee_builder.into_place(self);
{
let scrutinee_place = scrutinee_builder.into_place(self.tcx, self.typeck_results);
self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place); self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
} }
@ -348,12 +346,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// ``` // ```
let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None; let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None;
let scrutinee_place: Place<'tcx>; let scrutinee_place: Place<'tcx>;
if let Ok(scrutinee_builder) = scrutinee_place_builder if let Ok(scrutinee_builder) =
.clone() scrutinee_place_builder.clone().try_upvars_resolved(this)
.try_upvars_resolved(this.tcx, this.typeck_results)
{ {
scrutinee_place = scrutinee_place = scrutinee_builder.into_place(this);
scrutinee_builder.into_place(this.tcx, this.typeck_results);
opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span)); opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span));
} }
let scope = this.declare_bindings( let scope = this.declare_bindings(
@ -602,12 +598,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
while let Some(next) = { while let Some(next) = {
for binding in &candidate_ref.bindings { for binding in &candidate_ref.bindings {
let local = self.var_local_id(binding.var_id, OutsideGuard); let local = self.var_local_id(binding.var_id, OutsideGuard);
let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
)))) = self.local_decls[local].local_info else {
bug!("Let binding to non-user variable.")
};
// `try_upvars_resolved` may fail if it is unable to resolve the given // `try_upvars_resolved` may fail if it is unable to resolve the given
// `PlaceBuilder` inside a closure. In this case, we don't want to include // `PlaceBuilder` inside a closure. In this case, we don't want to include
// a scrutinee place. `scrutinee_place_builder` will fail for destructured // a scrutinee place. `scrutinee_place_builder` will fail for destructured
@ -622,10 +612,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// let (v1, v2) = foo; // let (v1, v2) = foo;
// }; // };
// ``` // ```
if let Ok(match_pair_resolved) = if let Ok(match_pair_resolved) = initializer.clone().try_upvars_resolved(self) {
initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results) let place = match_pair_resolved.into_place(self);
{
let place = match_pair_resolved.into_place(self.tcx, self.typeck_results); let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
)))) = self.local_decls[local].local_info else {
bug!("Let binding to non-user variable.")
};
*match_place = Some(place); *match_place = Some(place);
} }
} }
@ -654,6 +649,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// scope for the bindings in these patterns, if such a scope had to be /// scope for the bindings in these patterns, if such a scope had to be
/// created. NOTE: Declaring the bindings should always be done in their /// created. NOTE: Declaring the bindings should always be done in their
/// drop scope. /// drop scope.
#[instrument(skip(self), level = "debug")]
pub(crate) fn declare_bindings( pub(crate) fn declare_bindings(
&mut self, &mut self,
mut visibility_scope: Option<SourceScope>, mut visibility_scope: Option<SourceScope>,
@ -662,7 +658,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
has_guard: ArmHasGuard, has_guard: ArmHasGuard,
opt_match_place: Option<(Option<&Place<'tcx>>, Span)>, opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,
) -> Option<SourceScope> { ) -> Option<SourceScope> {
debug!("declare_bindings: pattern={:?}", pattern);
self.visit_primary_bindings( self.visit_primary_bindings(
&pattern, &pattern,
UserTypeProjections::none(), UserTypeProjections::none(),
@ -872,7 +867,7 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
Candidate { Candidate {
span: pattern.span, span: pattern.span,
has_guard, has_guard,
match_pairs: smallvec![MatchPair { place, pattern }], match_pairs: smallvec![MatchPair::new(place, pattern)],
bindings: Vec::new(), bindings: Vec::new(),
ascriptions: Vec::new(), ascriptions: Vec::new(),
subcandidates: Vec::new(), subcandidates: Vec::new(),
@ -1048,6 +1043,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// if `x.0` matches `false` (for the third arm). In the (impossible at /// if `x.0` matches `false` (for the third arm). In the (impossible at
/// runtime) case when `x.0` is now `true`, we branch to /// runtime) case when `x.0` is now `true`, we branch to
/// `otherwise_block`. /// `otherwise_block`.
#[instrument(skip(self, fake_borrows), level = "debug")]
fn match_candidates<'pat>( fn match_candidates<'pat>(
&mut self, &mut self,
span: Span, span: Span,
@ -1057,11 +1053,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
candidates: &mut [&mut Candidate<'pat, 'tcx>], candidates: &mut [&mut Candidate<'pat, 'tcx>],
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
) { ) {
debug!(
"matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})",
span, candidates, start_block, otherwise_block,
);
// Start by simplifying candidates. Once this process is complete, all // Start by simplifying candidates. Once this process is complete, all
// the match pairs which remain require some form of test, whether it // the match pairs which remain require some form of test, whether it
// be a switch or pattern comparison. // be a switch or pattern comparison.
@ -1380,6 +1371,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
) )
} }
#[instrument(
skip(self, otherwise, or_span, place, fake_borrows, candidate, pats),
level = "debug"
)]
fn test_or_pattern<'pat>( fn test_or_pattern<'pat>(
&mut self, &mut self,
candidate: &mut Candidate<'pat, 'tcx>, candidate: &mut Candidate<'pat, 'tcx>,
@ -1389,7 +1384,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
place: PlaceBuilder<'tcx>, place: PlaceBuilder<'tcx>,
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
) { ) {
debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats); debug!("candidate={:#?}\npats={:#?}", candidate, pats);
let mut or_candidates: Vec<_> = pats let mut or_candidates: Vec<_> = pats
.iter() .iter()
.map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard)) .map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard))
@ -1605,9 +1600,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Insert a Shallow borrow of any places that is switched on. // Insert a Shallow borrow of any places that is switched on.
if let Some(fb) = fake_borrows && let Ok(match_place_resolved) = if let Some(fb) = fake_borrows && let Ok(match_place_resolved) =
match_place.clone().try_upvars_resolved(self.tcx, self.typeck_results) match_place.clone().try_upvars_resolved(self)
{ {
let resolved_place = match_place_resolved.into_place(self.tcx, self.typeck_results); let resolved_place = match_place_resolved.into_place(self);
fb.insert(resolved_place); fb.insert(resolved_place);
} }
@ -1634,9 +1629,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
candidates = rest; candidates = rest;
} }
// at least the first candidate ought to be tested // at least the first candidate ought to be tested
assert!(total_candidate_count > candidates.len()); assert!(
debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len()); total_candidate_count > candidates.len(),
debug!("test_candidates: untested_candidates: {}", candidates.len()); "{}, {:#?}",
total_candidate_count,
candidates
);
debug!("tested_candidates: {}", total_candidate_count - candidates.len());
debug!("untested_candidates: {}", candidates.len());
// HACK(matthewjasper) This is a closure so that we can let the test // HACK(matthewjasper) This is a closure so that we can let the test
// create its blocks before the rest of the match. This currently // create its blocks before the rest of the match. This currently
@ -1794,10 +1794,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
); );
let mut opt_expr_place: Option<(Option<&Place<'tcx>>, Span)> = None; let mut opt_expr_place: Option<(Option<&Place<'tcx>>, Span)> = None;
let expr_place: Place<'tcx>; let expr_place: Place<'tcx>;
if let Ok(expr_builder) = if let Ok(expr_builder) = expr_place_builder.try_upvars_resolved(self) {
expr_place_builder.try_upvars_resolved(self.tcx, self.typeck_results) expr_place = expr_builder.into_place(self);
{
expr_place = expr_builder.into_place(self.tcx, self.typeck_results);
opt_expr_place = Some((Some(&expr_place), expr_span)); opt_expr_place = Some((Some(&expr_place), expr_span));
} }
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap(); let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
@ -2195,6 +2193,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// first local is a binding for occurrences of `var` in the guard, which /// first local is a binding for occurrences of `var` in the guard, which
/// will have type `&T`. The second local is a binding for occurrences of /// will have type `&T`. The second local is a binding for occurrences of
/// `var` in the arm body, which will have type `T`. /// `var` in the arm body, which will have type `T`.
#[instrument(skip(self), level = "debug")]
fn declare_binding( fn declare_binding(
&mut self, &mut self,
source_info: SourceInfo, source_info: SourceInfo,
@ -2209,19 +2208,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
opt_match_place: Option<(Option<Place<'tcx>>, Span)>, opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
pat_span: Span, pat_span: Span,
) { ) {
debug!(
"declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \
visibility_scope={:?}, source_info={:?})",
var_id, name, mode, var_ty, visibility_scope, source_info
);
let tcx = self.tcx; let tcx = self.tcx;
let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope }; let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope };
let binding_mode = match mode { let binding_mode = match mode {
BindingMode::ByValue => ty::BindingMode::BindByValue(mutability), BindingMode::ByValue => ty::BindingMode::BindByValue(mutability),
BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability), BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability),
}; };
debug!("declare_binding: user_ty={:?}", user_ty);
let local = LocalDecl::<'tcx> { let local = LocalDecl::<'tcx> {
mutability, mutability,
ty: var_ty, ty: var_ty,
@ -2271,7 +2263,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} else { } else {
LocalsForNode::One(for_arm_body) LocalsForNode::One(for_arm_body)
}; };
debug!("declare_binding: vars={:?}", locals); debug!(?locals);
self.var_indices.insert(var_id, locals); self.var_indices.insert(var_id, locals);
} }

View file

@ -37,12 +37,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// ///
/// only generates a single switch. If this happens this method returns /// only generates a single switch. If this happens this method returns
/// `true`. /// `true`.
#[instrument(skip(self, candidate), level = "debug")]
pub(super) fn simplify_candidate<'pat>( pub(super) fn simplify_candidate<'pat>(
&mut self, &mut self,
candidate: &mut Candidate<'pat, 'tcx>, candidate: &mut Candidate<'pat, 'tcx>,
) -> bool { ) -> bool {
// repeatedly simplify match pairs until fixed point is reached // repeatedly simplify match pairs until fixed point is reached
debug!(?candidate, "simplify_candidate"); debug!("{:#?}", candidate);
// existing_bindings and new_bindings exists to keep the semantics in order. // existing_bindings and new_bindings exists to keep the semantics in order.
// Reversing the binding order for bindings after `@` changes the binding order in places // Reversing the binding order for bindings after `@` changes the binding order in places
@ -155,12 +156,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ascription: thir::Ascription { ref annotation, variance }, ascription: thir::Ascription { ref annotation, variance },
} => { } => {
// Apply the type ascription to the value at `match_pair.place`, which is the // Apply the type ascription to the value at `match_pair.place`, which is the
if let Ok(place_resolved) = if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self) {
match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results)
{
candidate.ascriptions.push(Ascription { candidate.ascriptions.push(Ascription {
annotation: annotation.clone(), annotation: annotation.clone(),
source: place_resolved.into_place(self.tcx, self.typeck_results), source: place_resolved.into_place(self),
variance, variance,
}); });
} }
@ -184,12 +183,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ref subpattern, ref subpattern,
is_primary: _, is_primary: _,
} => { } => {
if let Ok(place_resolved) = if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self) {
match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results)
{
candidate.bindings.push(Binding { candidate.bindings.push(Binding {
span: match_pair.pattern.span, span: match_pair.pattern.span,
source: place_resolved.into_place(self.tcx, self.typeck_results), source: place_resolved.into_place(self),
var_id: var, var_id: var,
binding_mode: mode, binding_mode: mode,
}); });

View file

@ -144,6 +144,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
} }
#[instrument(skip(self, make_target_blocks, place_builder), level = "debug")]
pub(super) fn perform_test( pub(super) fn perform_test(
&mut self, &mut self,
match_start_span: Span, match_start_span: Span,
@ -153,21 +154,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
test: &Test<'tcx>, test: &Test<'tcx>,
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>, make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
) { ) {
let place: Place<'tcx>; let place = place_builder.into_place(self);
if let Ok(test_place_builder) = let place_ty = place.ty(&self.local_decls, self.tcx);
place_builder.try_upvars_resolved(self.tcx, self.typeck_results) debug!(?place, ?place_ty,);
{
place = test_place_builder.into_place(self.tcx, self.typeck_results);
} else {
return;
}
debug!(
"perform_test({:?}, {:?}: {:?}, {:?})",
block,
place,
place.ty(&self.local_decls, self.tcx),
test
);
let source_info = self.source_info(test.span); let source_info = self.source_info(test.span);
match test.kind { match test.kind {
@ -735,9 +724,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`, // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
// we want to create a set of derived match-patterns like // we want to create a set of derived match-patterns like
// `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`. // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
let elem = let downcast_place = match_pair.place.downcast(adt_def, variant_index); // `(x as Variant)`
ProjectionElem::Downcast(Some(adt_def.variant(variant_index).name), variant_index);
let downcast_place = match_pair.place.project(elem); // `(x as Variant)`
let consequent_match_pairs = subpatterns.iter().map(|subpattern| { let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
// e.g., `(x as Variant).0` // e.g., `(x as Variant).0`
let place = downcast_place.clone().field(subpattern.field, subpattern.pattern.ty); let place = downcast_place.clone().field(subpattern.field, subpattern.pattern.ty);

View file

@ -31,21 +31,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
suffix: &'pat [Pat<'tcx>], suffix: &'pat [Pat<'tcx>],
) { ) {
let tcx = self.tcx; let tcx = self.tcx;
let (min_length, exact_size) = if let Ok(place_resolved) = let (min_length, exact_size) =
place.clone().try_upvars_resolved(tcx, self.typeck_results) if let Ok(place_resolved) = place.clone().try_upvars_resolved(self) {
{ match place_resolved.into_place(self).ty(&self.local_decls, tcx).ty.kind() {
match place_resolved ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true),
.into_place(tcx, self.typeck_results) _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
.ty(&self.local_decls, tcx) }
.ty } else {
.kind() ((prefix.len() + suffix.len()).try_into().unwrap(), false)
{ };
ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true),
_ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
}
} else {
((prefix.len() + suffix.len()).try_into().unwrap(), false)
};
match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| { match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
let elem = let elem =
@ -100,10 +94,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
pub(crate) fn new( pub(in crate::build) fn new(
place: PlaceBuilder<'tcx>, place: PlaceBuilder<'tcx>,
pattern: &'pat Pat<'tcx>, pattern: &'pat Pat<'tcx>,
) -> MatchPair<'pat, 'tcx> { ) -> MatchPair<'pat, 'tcx> {
// Force the place type to the pattern's type.
// FIXME(oli-obk): only do this when we don't already know the place type.
// FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
let place = place.project(ProjectionElem::OpaqueCast(pattern.ty));
MatchPair { place, pattern } MatchPair { place, pattern }
} }
} }

View file

@ -553,6 +553,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Convenience wrapper that pushes a scope and then executes `f` /// Convenience wrapper that pushes a scope and then executes `f`
/// to build its contents, popping the scope afterwards. /// to build its contents, popping the scope afterwards.
#[instrument(skip(self, f), level = "debug")]
pub(crate) fn in_scope<F, R>( pub(crate) fn in_scope<F, R>(
&mut self, &mut self,
region_scope: (region::Scope, SourceInfo), region_scope: (region::Scope, SourceInfo),
@ -562,7 +563,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
where where
F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>, F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
{ {
debug!("in_scope(region_scope={:?})", region_scope);
let source_scope = self.source_scope; let source_scope = self.source_scope;
let tcx = self.tcx; let tcx = self.tcx;
if let LintLevel::Explicit(current_hir_id) = lint_level { if let LintLevel::Explicit(current_hir_id) = lint_level {
@ -589,7 +589,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let rv = unpack!(block = f(self)); let rv = unpack!(block = f(self));
unpack!(block = self.pop_scope(region_scope, block)); unpack!(block = self.pop_scope(region_scope, block));
self.source_scope = source_scope; self.source_scope = source_scope;
debug!("in_scope: exiting region_scope={:?} block={:?}", region_scope, block); debug!(?block);
block.and(rv) block.and(rv)
} }

View file

@ -48,6 +48,8 @@ impl<'tcx> Cx<'tcx> {
_ => None, _ => None,
}; };
trace!(?expr.ty);
// Now apply adjustments, if any. // Now apply adjustments, if any.
for adjustment in self.typeck_results.expr_adjustments(hir_expr) { for adjustment in self.typeck_results.expr_adjustments(hir_expr) {
trace!(?expr, ?adjustment); trace!(?expr, ?adjustment);
@ -56,6 +58,8 @@ impl<'tcx> Cx<'tcx> {
self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span)); self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span));
} }
trace!(?expr.ty, "after adjustments");
// Next, wrap this up in the expr's scope. // Next, wrap this up in the expr's scope.
expr = Expr { expr = Expr {
temp_lifetime, temp_lifetime,

View file

@ -1202,35 +1202,32 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
/// Creates a new list of wildcard fields for a given constructor. The result must have a /// Creates a new list of wildcard fields for a given constructor. The result must have a
/// length of `constructor.arity()`. /// length of `constructor.arity()`.
pub(super) fn wildcards( #[instrument(level = "trace")]
cx: &MatchCheckCtxt<'p, 'tcx>, pub(super) fn wildcards(pcx: PatCtxt<'_, 'p, 'tcx>, constructor: &Constructor<'tcx>) -> Self {
ty: Ty<'tcx>,
constructor: &Constructor<'tcx>,
) -> Self {
let ret = match constructor { let ret = match constructor {
Single | Variant(_) => match ty.kind() { Single | Variant(_) => match pcx.ty.kind() {
ty::Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter()), ty::Tuple(fs) => Fields::wildcards_from_tys(pcx.cx, fs.iter()),
ty::Ref(_, rty, _) => Fields::wildcards_from_tys(cx, once(*rty)), ty::Ref(_, rty, _) => Fields::wildcards_from_tys(pcx.cx, once(*rty)),
ty::Adt(adt, substs) => { ty::Adt(adt, substs) => {
if adt.is_box() { if adt.is_box() {
// The only legal patterns of type `Box` (outside `std`) are `_` and box // The only legal patterns of type `Box` (outside `std`) are `_` and box
// patterns. If we're here we can assume this is a box pattern. // patterns. If we're here we can assume this is a box pattern.
Fields::wildcards_from_tys(cx, once(substs.type_at(0))) Fields::wildcards_from_tys(pcx.cx, once(substs.type_at(0)))
} else { } else {
let variant = &adt.variant(constructor.variant_index_for_adt(*adt)); let variant = &adt.variant(constructor.variant_index_for_adt(*adt));
let tys = Fields::list_variant_nonhidden_fields(cx, ty, variant) let tys = Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant)
.map(|(_, ty)| ty); .map(|(_, ty)| ty);
Fields::wildcards_from_tys(cx, tys) Fields::wildcards_from_tys(pcx.cx, tys)
} }
} }
_ => bug!("Unexpected type for `Single` constructor: {:?}", ty), _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx),
}, },
Slice(slice) => match *ty.kind() { Slice(slice) => match *pcx.ty.kind() {
ty::Slice(ty) | ty::Array(ty, _) => { ty::Slice(ty) | ty::Array(ty, _) => {
let arity = slice.arity(); let arity = slice.arity();
Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty)) Fields::wildcards_from_tys(pcx.cx, (0..arity).map(|_| ty))
} }
_ => bug!("bad slice pattern {:?} {:?}", constructor, ty), _ => bug!("bad slice pattern {:?} {:?}", constructor, pcx),
}, },
Str(..) Str(..)
| FloatRange(..) | FloatRange(..)
@ -1243,7 +1240,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
bug!("called `Fields::wildcards` on an `Or` ctor") bug!("called `Fields::wildcards` on an `Or` ctor")
} }
}; };
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); debug!(?ret);
ret ret
} }
@ -1286,7 +1283,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
/// `Some(_)`. /// `Some(_)`.
pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self { pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self {
let fields = Fields::wildcards(pcx.cx, pcx.ty, &ctor); let fields = Fields::wildcards(pcx, &ctor);
DeconstructedPat::new(ctor, fields, pcx.ty, DUMMY_SP) DeconstructedPat::new(ctor, fields, pcx.ty, DUMMY_SP)
} }
@ -1553,13 +1550,13 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
/// `other_ctor` can be different from `self.ctor`, but must be covered by it. /// `other_ctor` can be different from `self.ctor`, but must be covered by it.
pub(super) fn specialize<'a>( pub(super) fn specialize<'a>(
&'a self, &'a self,
cx: &MatchCheckCtxt<'p, 'tcx>, pcx: PatCtxt<'_, 'p, 'tcx>,
other_ctor: &Constructor<'tcx>, other_ctor: &Constructor<'tcx>,
) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> { ) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> {
match (&self.ctor, other_ctor) { match (&self.ctor, other_ctor) {
(Wildcard, _) => { (Wildcard, _) => {
// We return a wildcard for each field of `other_ctor`. // We return a wildcard for each field of `other_ctor`.
Fields::wildcards(cx, self.ty, other_ctor).iter_patterns().collect() Fields::wildcards(pcx, other_ctor).iter_patterns().collect()
} }
(Slice(self_slice), Slice(other_slice)) (Slice(self_slice), Slice(other_slice))
if self_slice.arity() != other_slice.arity() => if self_slice.arity() != other_slice.arity() =>
@ -1578,7 +1575,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
let prefix = &self.fields.fields[..prefix]; let prefix = &self.fields.fields[..prefix];
let suffix = &self.fields.fields[self_slice.arity() - suffix..]; let suffix = &self.fields.fields[self_slice.arity() - suffix..];
let wildcard: &_ = let wildcard: &_ =
cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty)); pcx.cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty));
let extra_wildcards = other_slice.arity() - self_slice.arity(); let extra_wildcards = other_slice.arity() - self_slice.arity();
let extra_wildcards = (0..extra_wildcards).map(|_| wildcard); let extra_wildcards = (0..extra_wildcards).map(|_| wildcard);
prefix.iter().chain(extra_wildcards).chain(suffix).collect() prefix.iter().chain(extra_wildcards).chain(suffix).collect()

View file

@ -196,6 +196,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
} }
} }
#[instrument(skip(self), level = "debug")]
fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> {
let mut ty = self.typeck_results.node_type(pat.hir_id); let mut ty = self.typeck_results.node_type(pat.hir_id);

View file

@ -411,12 +411,12 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
/// This is roughly the inverse of `Constructor::apply`. /// This is roughly the inverse of `Constructor::apply`.
fn pop_head_constructor( fn pop_head_constructor(
&self, &self,
cx: &MatchCheckCtxt<'p, 'tcx>, pcx: PatCtxt<'_, 'p, 'tcx>,
ctor: &Constructor<'tcx>, ctor: &Constructor<'tcx>,
) -> PatStack<'p, 'tcx> { ) -> PatStack<'p, 'tcx> {
// We pop the head pattern and push the new fields extracted from the arguments of // We pop the head pattern and push the new fields extracted from the arguments of
// `self.head()`. // `self.head()`.
let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(cx, ctor); let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(pcx, ctor);
new_fields.extend_from_slice(&self.pats[1..]); new_fields.extend_from_slice(&self.pats[1..]);
PatStack::from_vec(new_fields) PatStack::from_vec(new_fields)
} }
@ -475,7 +475,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
let mut matrix = Matrix::empty(); let mut matrix = Matrix::empty();
for row in &self.patterns { for row in &self.patterns {
if ctor.is_covered_by(pcx, row.head().ctor()) { if ctor.is_covered_by(pcx, row.head().ctor()) {
let new_row = row.pop_head_constructor(pcx.cx, ctor); let new_row = row.pop_head_constructor(pcx, ctor);
matrix.push(new_row); matrix.push(new_row);
} }
} }
@ -786,7 +786,7 @@ fn is_useful<'p, 'tcx>(
is_under_guard: bool, is_under_guard: bool,
is_top_level: bool, is_top_level: bool,
) -> Usefulness<'p, 'tcx> { ) -> Usefulness<'p, 'tcx> {
debug!("matrix,v={:?}{:?}", matrix, v); debug!(?matrix, ?v);
let Matrix { patterns: rows, .. } = matrix; let Matrix { patterns: rows, .. } = matrix;
// The base case. We are pattern-matching on () and the return value is // The base case. We are pattern-matching on () and the return value is
@ -806,11 +806,6 @@ fn is_useful<'p, 'tcx>(
debug_assert!(rows.iter().all(|r| r.len() == v.len())); debug_assert!(rows.iter().all(|r| r.len() == v.len()));
let ty = v.head().ty();
let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span());
let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive };
// If the first pattern is an or-pattern, expand it. // If the first pattern is an or-pattern, expand it.
let mut ret = Usefulness::new_not_useful(witness_preference); let mut ret = Usefulness::new_not_useful(witness_preference);
if v.head().is_or_pat() { if v.head().is_or_pat() {
@ -832,6 +827,19 @@ fn is_useful<'p, 'tcx>(
} }
} }
} else { } else {
let mut ty = v.head().ty();
// Opaque types can't get destructured/split, but the patterns can
// actually hint at hidden types, so we use the patterns' types instead.
if let ty::Opaque(..) = v.head().ty().kind() {
if let Some(row) = rows.first() {
ty = row.head().ty();
}
}
let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span());
let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive };
let v_ctor = v.head().ctor(); let v_ctor = v.head().ctor();
debug!(?v_ctor); debug!(?v_ctor);
if let Constructor::IntRange(ctor_range) = &v_ctor { if let Constructor::IntRange(ctor_range) = &v_ctor {
@ -853,7 +861,7 @@ fn is_useful<'p, 'tcx>(
debug!("specialize({:?})", ctor); debug!("specialize({:?})", ctor);
// We cache the result of `Fields::wildcards` because it is used a lot. // We cache the result of `Fields::wildcards` because it is used a lot.
let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor); let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor);
let v = v.pop_head_constructor(cx, &ctor); let v = v.pop_head_constructor(pcx, &ctor);
let usefulness = ensure_sufficient_stack(|| { let usefulness = ensure_sufficient_stack(|| {
is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false) is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false)
}); });

View file

@ -48,6 +48,7 @@ impl<'tcx> Lift for PlaceElem<'tcx> {
match *self { match *self {
ProjectionElem::Deref => ProjectionElem::Deref, ProjectionElem::Deref => ProjectionElem::Deref,
ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty.lift()), ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty.lift()),
ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty.lift()),
ProjectionElem::Index(ref i) => ProjectionElem::Index(i.lift()), ProjectionElem::Index(ref i) => ProjectionElem::Index(i.lift()),
ProjectionElem::Subslice { from, to, from_end } => { ProjectionElem::Subslice { from, to, from_end } => {
ProjectionElem::Subslice { from, to, from_end } ProjectionElem::Subslice { from, to, from_end }

View file

@ -28,6 +28,7 @@ fn is_stable(place: PlaceRef<'_>) -> bool {
ProjectionElem::Field { .. } | ProjectionElem::Field { .. } |
ProjectionElem::ConstantIndex { .. } | ProjectionElem::ConstantIndex { .. } |
ProjectionElem::Subslice { .. } | ProjectionElem::Subslice { .. } |
ProjectionElem::OpaqueCast { .. } |
ProjectionElem::Downcast { .. } => true, ProjectionElem::Downcast { .. } => true,
} }
}) })

View file

@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" } rustc_index = { path = "../rustc_index" }
rustc_session = { path = "../rustc_session" } rustc_session = { path = "../rustc_session" }
rustc_target = { path = "../rustc_target" } rustc_target = { path = "../rustc_target" }
rustc_macros = { path = "../rustc_macros" }
rustc_ast = { path = "../rustc_ast" } rustc_ast = { path = "../rustc_ast" }
rustc_serialize = { path = "../rustc_serialize" } rustc_serialize = { path = "../rustc_serialize" }
rustc_span = { path = "../rustc_span" } rustc_span = { path = "../rustc_span" }

View file

@ -4,9 +4,10 @@
//! conflicts between multiple such attributes attached to the same //! conflicts between multiple such attributes attached to the same
//! item. //! item.
use crate::errors;
use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; use rustc_errors::{fluent, pluralize, struct_span_err, Applicability, MultiSpan};
use rustc_expand::base::resolve_path; use rustc_expand::base::resolve_path;
use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
use rustc_hir as hir; use rustc_hir as hir;
@ -175,16 +176,20 @@ impl CheckAttrVisitor<'_> {
if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
{ {
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { match attr.style {
let msg = match attr.style { ast::AttrStyle::Outer => self.tcx.emit_spanned_lint(
ast::AttrStyle::Outer => { UNUSED_ATTRIBUTES,
"crate-level attribute should be an inner attribute: add an exclamation \ hir_id,
mark: `#![foo]`" attr.span,
} errors::OuterCrateLevelAttr,
ast::AttrStyle::Inner => "crate-level attribute should be in the root module", ),
}; ast::AttrStyle::Inner => self.tcx.emit_spanned_lint(
lint.build(msg).emit(); UNUSED_ATTRIBUTES,
}); hir_id,
attr.span,
errors::InnerCrateLevelAttr,
),
}
} }
} }
@ -209,37 +214,21 @@ impl CheckAttrVisitor<'_> {
} }
fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { self.tcx.emit_spanned_lint(
lint.build(&format!( UNUSED_ATTRIBUTES,
"`#[{sym}]` is ignored on struct fields, match arms and macro defs", hir_id,
)) attr.span,
.warn( errors::IgnoredAttrWithMacro { sym },
"this was previously accepted by the compiler but is \ );
being phased out; it will become a hard error in \
a future release!",
)
.note(
"see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
for more information",
)
.emit();
});
} }
fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { self.tcx.emit_spanned_lint(
lint.build(&format!("`#[{sym}]` is ignored on struct fields and match arms")) UNUSED_ATTRIBUTES,
.warn( hir_id,
"this was previously accepted by the compiler but is \ attr.span,
being phased out; it will become a hard error in \ errors::IgnoredAttr { sym },
a future release!", );
)
.note(
"see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
for more information",
)
.emit();
});
} }
/// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
@ -249,9 +238,12 @@ impl CheckAttrVisitor<'_> {
| Target::Closure | Target::Closure
| Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { self.tcx.emit_spanned_lint(
lint.build("`#[inline]` is ignored on function prototypes").emit(); UNUSED_ATTRIBUTES,
}); hir_id,
attr.span,
errors::IgnoredInlineAttrFnProto,
);
true true
} }
// FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
@ -259,19 +251,12 @@ impl CheckAttrVisitor<'_> {
// accidentally, to to be compatible with crates depending on them, we can't throw an // accidentally, to to be compatible with crates depending on them, we can't throw an
// error here. // error here.
Target::AssocConst => { Target::AssocConst => {
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { self.tcx.emit_spanned_lint(
lint.build("`#[inline]` is ignored on constants") UNUSED_ATTRIBUTES,
.warn( hir_id,
"this was previously accepted by the compiler but is \ attr.span,
being phased out; it will become a hard error in \ errors::IgnoredInlineAttrConstants,
a future release!", );
)
.note(
"see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
for more information",
)
.emit();
});
true true
} }
// FIXME(#80564): Same for fields, arms, and macro defs // FIXME(#80564): Same for fields, arms, and macro defs
@ -280,14 +265,10 @@ impl CheckAttrVisitor<'_> {
true true
} }
_ => { _ => {
struct_span_err!( self.tcx.sess.emit_err(errors::InlineNotFnOrClosure {
self.tcx.sess, attr_span: attr.span,
attr.span, defn_span: span,
E0518, });
"attribute should be applied to function or closure",
)
.span_label(span, "not a function or closure")
.emit();
false false
} }
} }
@ -309,36 +290,40 @@ impl CheckAttrVisitor<'_> {
// function prototypes can't be covered // function prototypes can't be covered
Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { self.tcx.emit_spanned_lint(
lint.build("`#[no_coverage]` is ignored on function prototypes").emit(); UNUSED_ATTRIBUTES,
}); hir_id,
attr.span,
errors::IgnoredNoCoverageFnProto,
);
true true
} }
Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => { Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => {
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { self.tcx.emit_spanned_lint(
lint.build("`#[no_coverage]` does not propagate into items and must be applied to the contained functions directly").emit(); UNUSED_ATTRIBUTES,
}); hir_id,
attr.span,
errors::IgnoredNoCoveragePropagate,
);
true true
} }
Target::Expression | Target::Statement | Target::Arm => { Target::Expression | Target::Statement | Target::Arm => {
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { self.tcx.emit_spanned_lint(
lint.build("`#[no_coverage]` may only be applied to function definitions") UNUSED_ATTRIBUTES,
.emit(); hir_id,
}); attr.span,
errors::IgnoredNoCoverageFnDefn,
);
true true
} }
_ => { _ => {
struct_span_err!( self.tcx.sess.emit_err(errors::IgnoredNoCoverageNotCoverable {
self.tcx.sess, attr_span: attr.span,
attr.span, defn_span: span,
E0788, });
"`#[no_coverage]` must be applied to coverable code",
)
.span_label(span, "not coverable code")
.emit();
false false
} }
} }
@ -389,14 +374,10 @@ impl CheckAttrVisitor<'_> {
true true
} }
_ => { _ => {
self.tcx self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
.sess attr_span: attr.span,
.struct_span_err( defn_span: span,
attr.span, });
"attribute should be applied to a function definition",
)
.span_label(span, "not a function definition")
.emit();
false false
} }
} }
@ -408,14 +389,10 @@ impl CheckAttrVisitor<'_> {
Target::Fn Target::Fn
| Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
_ => { _ => {
self.tcx self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
.sess attr_span: attr.span,
.struct_span_err( defn_span: span,
attr.span, });
"attribute should be applied to a function definition",
)
.span_label(span, "not a function definition")
.emit();
false false
} }
} }
@ -432,13 +409,7 @@ impl CheckAttrVisitor<'_> {
) -> bool { ) -> bool {
match target { match target {
_ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => { _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
struct_span_err!( self.tcx.sess.emit_err(errors::NakedTrackedCaller { attr_span });
self.tcx.sess,
attr_span,
E0736,
"cannot use `#[track_caller]` with `#[naked]`",
)
.emit();
false false
} }
Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true, Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
@ -453,14 +424,9 @@ impl CheckAttrVisitor<'_> {
true true
} }
_ => { _ => {
struct_span_err!( self.tcx
self.tcx.sess, .sess
attr_span, .emit_err(errors::TrackedCallerWrongLocation { attr_span, defn_span: span });
E0739,
"attribute should be applied to function"
)
.span_label(span, "not a function")
.emit();
false false
} }
} }
@ -485,14 +451,10 @@ impl CheckAttrVisitor<'_> {
true true
} }
_ => { _ => {
struct_span_err!( self.tcx.sess.emit_err(errors::NonExhaustiveWrongLocation {
self.tcx.sess, attr_span: attr.span,
attr.span, defn_span: span,
E0701, });
"attribute can only be applied to a struct or enum"
)
.span_label(span, "not a struct or enum")
.emit();
false false
} }
} }
@ -511,11 +473,10 @@ impl CheckAttrVisitor<'_> {
true true
} }
_ => { _ => {
self.tcx self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
.sess attr_span: attr.span,
.struct_span_err(attr.span, "attribute can only be applied to a trait") defn_span: span,
.span_label(span, "not a trait") });
.emit();
false false
} }
} }
@ -531,11 +492,10 @@ impl CheckAttrVisitor<'_> {
match target { match target {
Target::Trait => true, Target::Trait => true,
_ => { _ => {
self.tcx self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
.sess attr_span: attr.span,
.struct_span_err(attr.span, "attribute can only be applied to a trait") defn_span: span,
.span_label(span, "not a trait") });
.emit();
false false
} }
} }
@ -555,16 +515,12 @@ impl CheckAttrVisitor<'_> {
// FIXME: #[target_feature] was previously erroneously allowed on statements and some // FIXME: #[target_feature] was previously erroneously allowed on statements and some
// crates used this, so only emit a warning. // crates used this, so only emit a warning.
Target::Statement => { Target::Statement => {
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { self.tcx.emit_spanned_lint(
lint.build("attribute should be applied to a function") UNUSED_ATTRIBUTES,
.warn( hir_id,
"this was previously accepted by the compiler but is \ attr.span,
being phased out; it will become a hard error in \ errors::TargetFeatureOnStatement,
a future release!", );
)
.span_label(span, "not a function")
.emit();
});
true true
} }
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
@ -576,11 +532,10 @@ impl CheckAttrVisitor<'_> {
true true
} }
_ => { _ => {
self.tcx self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
.sess attr_span: attr.span,
.struct_span_err(attr.span, "attribute should be applied to a function") defn_span: span,
.span_label(span, "not a function") });
.emit();
false false
} }
} }
@ -591,24 +546,17 @@ impl CheckAttrVisitor<'_> {
match target { match target {
Target::ForeignStatic | Target::Static => true, Target::ForeignStatic | Target::Static => true,
_ => { _ => {
self.tcx self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToStatic {
.sess attr_span: attr.span,
.struct_span_err(attr.span, "attribute should be applied to a static") defn_span: span,
.span_label(span, "not a static") });
.emit();
false false
} }
} }
} }
fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) { fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) {
self.tcx self.tcx.sess.emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name });
.sess
.struct_span_err(
meta.span(),
&format!("doc {0} attribute expects a string: #[doc({0} = \"a\")]", attr_name),
)
.emit();
} }
fn check_doc_alias_value( fn check_doc_alias_value(
@ -621,22 +569,12 @@ impl CheckAttrVisitor<'_> {
aliases: &mut FxHashMap<String, Span>, aliases: &mut FxHashMap<String, Span>,
) -> bool { ) -> bool {
let tcx = self.tcx; let tcx = self.tcx;
let err_fn = move |span: Span, msg: &str| { let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span());
tcx.sess.span_err( let attr_str =
span, &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" });
&format!(
"`#[doc(alias{})]` {}",
if is_list { "(\"...\")" } else { " = \"...\"" },
msg,
),
);
false
};
if doc_alias == kw::Empty { if doc_alias == kw::Empty {
return err_fn( tcx.sess.emit_err(errors::DocAliasEmpty { span, attr_str });
meta.name_value_literal_span().unwrap_or_else(|| meta.span()), return false;
"attribute cannot have empty value",
);
} }
let doc_alias_str = doc_alias.as_str(); let doc_alias_str = doc_alias.as_str();
@ -644,23 +582,16 @@ impl CheckAttrVisitor<'_> {
.chars() .chars()
.find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' ')) .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
{ {
self.tcx.sess.span_err( tcx.sess.emit_err(errors::DocAliasBadChar { span, attr_str, char_: c });
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
&format!(
"{:?} character isn't allowed in `#[doc(alias{})]`",
c,
if is_list { "(\"...\")" } else { " = \"...\"" },
),
);
return false; return false;
} }
if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') { if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') {
return err_fn( tcx.sess.emit_err(errors::DocAliasStartEnd { span, attr_str });
meta.name_value_literal_span().unwrap_or_else(|| meta.span()), return false;
"cannot start or end with ' '",
);
} }
if let Some(err) = match target {
let span = meta.span();
if let Some(location) = match target {
Target::Impl => Some("implementation block"), Target::Impl => Some("implementation block"),
Target::ForeignMod => Some("extern block"), Target::ForeignMod => Some("extern block"),
Target::AssocTy => { Target::AssocTy => {
@ -686,19 +617,21 @@ impl CheckAttrVisitor<'_> {
Target::Param => return false, Target::Param => return false,
_ => None, _ => None,
} { } {
return err_fn(meta.span(), &format!("isn't allowed on {}", err)); tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location });
return false;
} }
let item_name = self.tcx.hir().name(hir_id); let item_name = self.tcx.hir().name(hir_id);
if item_name == doc_alias { if item_name == doc_alias {
return err_fn(meta.span(), "is the same as the item's name"); tcx.sess.emit_err(errors::DocAliasNotAnAlias { span, attr_str });
return false;
} }
let span = meta.span();
if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) { if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) {
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, span, |lint| { self.tcx.emit_spanned_lint(
lint.build("doc alias is duplicated") UNUSED_ATTRIBUTES,
.span_label(*entry.entry.get(), "first defined here") hir_id,
.emit(); span,
}); errors::DocAliasDuplicated { first_defn: *entry.entry.get() },
);
} }
true true
} }
@ -723,22 +656,12 @@ impl CheckAttrVisitor<'_> {
_ => { _ => {
self.tcx self.tcx
.sess .sess
.struct_span_err( .emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
v.span(),
"`#[doc(alias(\"a\"))]` expects string literals",
)
.emit();
errors += 1; errors += 1;
} }
}, },
None => { None => {
self.tcx self.tcx.sess.emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
.sess
.struct_span_err(
v.span(),
"`#[doc(alias(\"a\"))]` expects string literals",
)
.emit();
errors += 1; errors += 1;
} }
} }
@ -747,14 +670,7 @@ impl CheckAttrVisitor<'_> {
} else if let Some(doc_alias) = meta.value_str() { } else if let Some(doc_alias) = meta.value_str() {
self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases) self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases)
} else { } else {
self.tcx self.tcx.sess.emit_err(errors::DocAliasMalformed { span: meta.span() });
.sess
.struct_span_err(
meta.span(),
"doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \
strings `#[doc(alias(\"a\", \"b\"))]`",
)
.emit();
false false
} }
} }
@ -771,35 +687,20 @@ impl CheckAttrVisitor<'_> {
}) { }) {
Some(ItemKind::Mod(ref module)) => { Some(ItemKind::Mod(ref module)) => {
if !module.item_ids.is_empty() { if !module.item_ids.is_empty() {
self.tcx self.tcx.sess.emit_err(errors::DocKeywordEmptyMod { span: meta.span() });
.sess
.struct_span_err(
meta.span(),
"`#[doc(keyword = \"...\")]` can only be used on empty modules",
)
.emit();
return false; return false;
} }
} }
_ => { _ => {
self.tcx self.tcx.sess.emit_err(errors::DocKeywordNotMod { span: meta.span() });
.sess
.struct_span_err(
meta.span(),
"`#[doc(keyword = \"...\")]` can only be used on modules",
)
.emit();
return false; return false;
} }
} }
if !rustc_lexer::is_ident(doc_keyword.as_str()) { if !rustc_lexer::is_ident(doc_keyword.as_str()) {
self.tcx self.tcx.sess.emit_err(errors::DocKeywordInvalidIdent {
.sess span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
.struct_span_err( doc_keyword,
meta.name_value_literal_span().unwrap_or_else(|| meta.span()), });
&format!("`{doc_keyword}` is not a valid identifier"),
)
.emit();
return false; return false;
} }
true true
@ -812,24 +713,12 @@ impl CheckAttrVisitor<'_> {
}) { }) {
Some(ItemKind::Impl(ref i)) => { Some(ItemKind::Impl(ref i)) => {
if !matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) { if !matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) {
self.tcx self.tcx.sess.emit_err(errors::DocTupleVariadicNotFirst { span: meta.span() });
.sess
.struct_span_err(
meta.span(),
"`#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity",
)
.emit();
return false; return false;
} }
} }
_ => { _ => {
self.tcx self.tcx.sess.emit_err(errors::DocKeywordOnlyImpl { span: meta.span() });
.sess
.struct_span_err(
meta.span(),
"`#[doc(keyword = \"...\")]` can only be used on impl blocks",
)
.emit();
return false; return false;
} }
} }
@ -858,13 +747,9 @@ impl CheckAttrVisitor<'_> {
if let Some((prev_inline, prev_span)) = *specified_inline { if let Some((prev_inline, prev_span)) = *specified_inline {
if do_inline != prev_inline { if do_inline != prev_inline {
let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]); let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
spans.push_span_label(prev_span, "this attribute..."); spans.push_span_label(prev_span, fluent::passes::doc_inline_conflict_first);
spans.push_span_label(meta.span(), "...conflicts with this attribute"); spans.push_span_label(meta.span(), fluent::passes::doc_inline_conflict_second);
self.tcx self.tcx.sess.emit_err(errors::DocKeywordConflict { spans });
.sess
.struct_span_err(spans, "conflicting doc inlining attributes")
.help("remove one of the conflicting attributes")
.emit();
return false; return false;
} }
true true
@ -873,23 +758,14 @@ impl CheckAttrVisitor<'_> {
true true
} }
} else { } else {
self.tcx.struct_span_lint_hir( self.tcx.emit_spanned_lint(
INVALID_DOC_ATTRIBUTES, INVALID_DOC_ATTRIBUTES,
hir_id, hir_id,
meta.span(), meta.span(),
|lint| { errors::DocInlineOnlyUse {
let mut err = lint.build( attr_span: meta.span(),
"this attribute can only be applied to a `use` item", item_span: (attr.style == AttrStyle::Outer)
); .then(|| self.tcx.hir().span(hir_id)),
err.span_label(meta.span(), "only applicable on `use` items");
if attr.style == AttrStyle::Outer {
err.span_label(
self.tcx.hir().span(hir_id),
"not a `use` item",
);
}
err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information")
.emit();
}, },
); );
false false
@ -904,15 +780,7 @@ impl CheckAttrVisitor<'_> {
attr_name: &str, attr_name: &str,
) -> bool { ) -> bool {
if CRATE_HIR_ID == hir_id { if CRATE_HIR_ID == hir_id {
self.tcx self.tcx.sess.emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name });
.sess
.struct_span_err(
meta.span(),
&format!(
"`#![doc({attr_name} = \"...\")]` isn't allowed as a crate-level attribute",
),
)
.emit();
return false; return false;
} }
true true
@ -926,36 +794,25 @@ impl CheckAttrVisitor<'_> {
hir_id: HirId, hir_id: HirId,
) -> bool { ) -> bool {
if hir_id != CRATE_HIR_ID { if hir_id != CRATE_HIR_ID {
self.tcx.struct_span_lint_hir( self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| {
INVALID_DOC_ATTRIBUTES, let mut err = lint.build(fluent::passes::attr_crate_level);
hir_id, if attr.style == AttrStyle::Outer
meta.span(), && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID
|lint| { {
let mut err = lint.build( if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) {
"this attribute can only be applied at the crate level", src.insert(1, '!');
); err.span_suggestion_verbose(
if attr.style == AttrStyle::Outer && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID { attr.span,
if let Ok(mut src) = fluent::passes::suggestion,
self.tcx.sess.source_map().span_to_snippet(attr.span) src,
{ Applicability::MaybeIncorrect,
src.insert(1, '!'); );
err.span_suggestion_verbose( } else {
attr.span, err.span_help(attr.span, fluent::passes::help);
"to apply to the crate, use an inner attribute",
src,
Applicability::MaybeIncorrect,
);
} else {
err.span_help(
attr.span,
"to apply to the crate, use an inner attribute",
);
}
} }
err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information") }
.emit(); err.note(fluent::passes::note).emit();
}, });
);
return false; return false;
} }
true true
@ -970,18 +827,14 @@ impl CheckAttrVisitor<'_> {
match i_meta.name_or_empty() { match i_meta.name_or_empty() {
sym::attr | sym::no_crate_inject => {} sym::attr | sym::no_crate_inject => {}
_ => { _ => {
self.tcx.struct_span_lint_hir( self.tcx.emit_spanned_lint(
INVALID_DOC_ATTRIBUTES, INVALID_DOC_ATTRIBUTES,
hir_id, hir_id,
i_meta.span(), i_meta.span(),
|lint| { errors::DocTestUnknown {
lint.build(&format!( path: rustc_ast_pretty::pprust::path_to_string(
"unknown `doc(test)` attribute `{}`", &i_meta.meta_item().unwrap().path,
rustc_ast_pretty::pprust::path_to_string( ),
&i_meta.meta_item().unwrap().path
),
))
.emit();
}, },
); );
is_valid = false; is_valid = false;
@ -989,9 +842,12 @@ impl CheckAttrVisitor<'_> {
} }
} }
} else { } else {
self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| { self.tcx.emit_spanned_lint(
lint.build("`#[doc(test(...)]` takes a list of attributes").emit(); INVALID_DOC_ATTRIBUTES,
}); hir_id,
meta.span(),
errors::DocTestTakesList,
);
is_valid = false; is_valid = false;
} }
is_valid is_valid
@ -1093,79 +949,66 @@ impl CheckAttrVisitor<'_> {
sym::primitive => { sym::primitive => {
if !self.tcx.features().rustdoc_internals { if !self.tcx.features().rustdoc_internals {
self.tcx.struct_span_lint_hir( self.tcx.emit_spanned_lint(
INVALID_DOC_ATTRIBUTES, INVALID_DOC_ATTRIBUTES,
hir_id, hir_id,
i_meta.span, i_meta.span,
|lint| { errors::DocPrimitive,
let mut diag = lint.build(
"`doc(primitive)` should never have been stable",
);
diag.emit();
},
); );
} }
} }
_ => { _ => {
self.tcx.struct_span_lint_hir( let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path);
INVALID_DOC_ATTRIBUTES, if i_meta.has_name(sym::spotlight) {
hir_id, self.tcx.emit_spanned_lint(
i_meta.span, INVALID_DOC_ATTRIBUTES,
|lint| { hir_id,
let mut diag = lint.build(&format!( i_meta.span,
"unknown `doc` attribute `{}`", errors::DocTestUnknownSpotlight {
rustc_ast_pretty::pprust::path_to_string(&i_meta.path), path,
)); span: i_meta.span
if i_meta.has_name(sym::spotlight) {
diag.note(
"`doc(spotlight)` was renamed to `doc(notable_trait)`",
);
diag.span_suggestion_short(
i_meta.span,
"use `notable_trait` instead",
"notable_trait",
Applicability::MachineApplicable,
);
diag.note("`doc(spotlight)` is now a no-op");
} }
if i_meta.has_name(sym::include) { );
if let Some(value) = i_meta.value_str() { } else if i_meta.has_name(sym::include) &&
// if there are multiple attributes, the suggestion would suggest deleting all of them, which is incorrect let Some(value) = i_meta.value_str() {
let applicability = if list.len() == 1 { let applicability = if list.len() == 1 {
Applicability::MachineApplicable Applicability::MachineApplicable
} else { } else {
Applicability::MaybeIncorrect Applicability::MaybeIncorrect
}; };
let inner = if attr.style == AttrStyle::Inner { // If there are multiple attributes, the suggestion would suggest
"!" // deleting all of them, which is incorrect.
} else { self.tcx.emit_spanned_lint(
"" INVALID_DOC_ATTRIBUTES,
}; hir_id,
diag.span_suggestion( i_meta.span,
attr.meta().unwrap().span, errors::DocTestUnknownInclude {
"use `doc = include_str!` instead", path,
format!( value: value.to_string(),
"#{inner}[doc = include_str!(\"{value}\")]", inner: (attr.style == AttrStyle::Inner)
), .then_some("!")
applicability, .unwrap_or(""),
); sugg: (attr.meta().unwrap().span, applicability),
}
} }
diag.emit(); );
}, } else {
); self.tcx.emit_spanned_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
i_meta.span,
errors::DocTestUnknownAny { path }
);
}
is_valid = false; is_valid = false;
} }
} }
} else { } else {
self.tcx.struct_span_lint_hir( self.tcx.emit_spanned_lint(
INVALID_DOC_ATTRIBUTES, INVALID_DOC_ATTRIBUTES,
hir_id, hir_id,
meta.span(), meta.span(),
|lint| { errors::DocInvalid,
lint.build("invalid `doc` attribute").emit();
},
); );
is_valid = false; is_valid = false;
} }
@ -1180,14 +1023,7 @@ impl CheckAttrVisitor<'_> {
match target { match target {
Target::Struct | Target::Enum | Target::TyAlias => true, Target::Struct | Target::Enum | Target::TyAlias => true,
_ => { _ => {
self.tcx self.tcx.sess.emit_err(errors::PassByValue { attr_span: attr.span, span });
.sess
.struct_span_err(
attr.span,
"`pass_by_value` attribute should be applied to a struct, enum or type alias.",
)
.span_label(span, "is not a struct, enum or type alias")
.emit();
false false
} }
} }
@ -1197,14 +1033,7 @@ impl CheckAttrVisitor<'_> {
match target { match target {
Target::Method(MethodKind::Inherent) => true, Target::Method(MethodKind::Inherent) => true,
_ => { _ => {
self.tcx self.tcx.sess.emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span });
.sess
.struct_span_err(
attr.span,
"`rustc_allow_incoherent_impl` attribute should be applied to impl items.",
)
.span_label(span, "the only currently supported targets are inherent methods")
.emit();
false false
} }
} }
@ -1223,12 +1052,7 @@ impl CheckAttrVisitor<'_> {
_ => { _ => {
self.tcx self.tcx
.sess .sess
.struct_span_err( .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span });
attr.span,
"`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.",
)
.span_label(span, "only adts, extern types and traits are supported")
.emit();
false false
} }
} }
@ -1238,19 +1062,12 @@ impl CheckAttrVisitor<'_> {
fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
let node = self.tcx.hir().get(hir_id); let node = self.tcx.hir().get(hir_id);
if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() { if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() {
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { self.tcx.emit_spanned_lint(
lint.build( UNUSED_ATTRIBUTES,
"`must_use` attribute on `async` functions \ hir_id,
applies to the anonymous `Future` returned by the \ attr.span,
function, not the value within", errors::MustUseAsync { span }
) );
.span_label(
span,
"this attribute does nothing, the `Future`s \
returned by async functions are already `must_use`",
)
.emit();
});
} }
if !matches!( if !matches!(
@ -1278,12 +1095,12 @@ impl CheckAttrVisitor<'_> {
_ => "a", _ => "a",
}; };
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { self.tcx.emit_spanned_lint(
lint.build(&format!( UNUSED_ATTRIBUTES,
"`#[must_use]` has no effect when applied to {article} {target}" hir_id,
)) attr.span,
.emit(); errors::MustUseNoEffect { article, target },
}); );
} }
// For now, its always valid // For now, its always valid
@ -1295,11 +1112,7 @@ impl CheckAttrVisitor<'_> {
match target { match target {
Target::Struct | Target::Enum | Target::Union | Target::Trait => true, Target::Struct | Target::Enum | Target::Union | Target::Trait => true,
_ => { _ => {
self.tcx self.tcx.sess.emit_err(errors::MustNotSuspend { attr_span: attr.span, span });
.sess
.struct_span_err(attr.span, "`must_not_suspend` attribute should be applied to a struct, enum, or trait")
.span_label(span, "is not a struct, enum, or trait")
.emit();
false false
} }
} }
@ -1319,16 +1132,12 @@ impl CheckAttrVisitor<'_> {
_ => { _ => {
// FIXME: #[cold] was previously allowed on non-functions and some crates used // FIXME: #[cold] was previously allowed on non-functions and some crates used
// this, so only emit a warning. // this, so only emit a warning.
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { self.tcx.emit_spanned_lint(
lint.build("attribute should be applied to a function") UNUSED_ATTRIBUTES,
.warn( hir_id,
"this was previously accepted by the compiler but is \ attr.span,
being phased out; it will become a hard error in \ errors::Cold { span },
a future release!", );
)
.span_label(span, "not a function")
.emit();
});
} }
} }
} }
@ -1343,19 +1152,12 @@ impl CheckAttrVisitor<'_> {
return; return;
} }
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { self.tcx.emit_spanned_lint(
let mut diag = UNUSED_ATTRIBUTES,
lint.build("attribute should be applied to an `extern` block with non-Rust ABI"); hir_id,
diag.warn( attr.span,
"this was previously accepted by the compiler but is \ errors::Link { span: (target != Target::ForeignMod).then_some(span) },
being phased out; it will become a hard error in \ );
a future release!",
);
if target != Target::ForeignMod {
diag.span_label(span, "not an `extern` block");
}
diag.emit();
});
} }
/// Checks if `#[link_name]` is applied to an item other than a foreign function or static. /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.

View file

@ -0,0 +1,362 @@
use rustc_errors::{Applicability, MultiSpan};
use rustc_macros::{LintDiagnostic, SessionDiagnostic};
use rustc_span::{Span, Symbol};
#[derive(LintDiagnostic)]
#[lint(passes::outer_crate_level_attr)]
pub struct OuterCrateLevelAttr;
#[derive(LintDiagnostic)]
#[lint(passes::inner_crate_level_attr)]
pub struct InnerCrateLevelAttr;
#[derive(LintDiagnostic)]
#[lint(passes::ignored_attr_with_macro)]
pub struct IgnoredAttrWithMacro<'a> {
pub sym: &'a str,
}
#[derive(LintDiagnostic)]
#[lint(passes::ignored_attr)]
pub struct IgnoredAttr<'a> {
pub sym: &'a str,
}
#[derive(LintDiagnostic)]
#[lint(passes::inline_ignored_function_prototype)]
pub struct IgnoredInlineAttrFnProto;
#[derive(LintDiagnostic)]
#[lint(passes::inline_ignored_constants)]
#[warn_]
#[note]
pub struct IgnoredInlineAttrConstants;
#[derive(SessionDiagnostic)]
#[error(passes::inline_not_fn_or_closure, code = "E0518")]
pub struct InlineNotFnOrClosure {
#[primary_span]
pub attr_span: Span,
#[label]
pub defn_span: Span,
}
#[derive(LintDiagnostic)]
#[lint(passes::no_coverage_ignored_function_prototype)]
pub struct IgnoredNoCoverageFnProto;
#[derive(LintDiagnostic)]
#[lint(passes::no_coverage_propagate)]
pub struct IgnoredNoCoveragePropagate;
#[derive(LintDiagnostic)]
#[lint(passes::no_coverage_fn_defn)]
pub struct IgnoredNoCoverageFnDefn;
#[derive(SessionDiagnostic)]
#[error(passes::no_coverage_not_coverable, code = "E0788")]
pub struct IgnoredNoCoverageNotCoverable {
#[primary_span]
pub attr_span: Span,
#[label]
pub defn_span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::should_be_applied_to_fn)]
pub struct AttrShouldBeAppliedToFn {
#[primary_span]
pub attr_span: Span,
#[label]
pub defn_span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::naked_tracked_caller, code = "E0736")]
pub struct NakedTrackedCaller {
#[primary_span]
pub attr_span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::should_be_applied_to_fn, code = "E0739")]
pub struct TrackedCallerWrongLocation {
#[primary_span]
pub attr_span: Span,
#[label]
pub defn_span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::should_be_applied_to_struct_enum, code = "E0701")]
pub struct NonExhaustiveWrongLocation {
#[primary_span]
pub attr_span: Span,
#[label]
pub defn_span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::should_be_applied_to_trait)]
pub struct AttrShouldBeAppliedToTrait {
#[primary_span]
pub attr_span: Span,
#[label]
pub defn_span: Span,
}
#[derive(LintDiagnostic)]
#[lint(passes::target_feature_on_statement)]
pub struct TargetFeatureOnStatement;
#[derive(SessionDiagnostic)]
#[error(passes::should_be_applied_to_static)]
pub struct AttrShouldBeAppliedToStatic {
#[primary_span]
pub attr_span: Span,
#[label]
pub defn_span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_expect_str)]
pub struct DocExpectStr<'a> {
#[primary_span]
pub attr_span: Span,
pub attr_name: &'a str,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_alias_empty)]
pub struct DocAliasEmpty<'a> {
#[primary_span]
pub span: Span,
pub attr_str: &'a str,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_alias_bad_char)]
pub struct DocAliasBadChar<'a> {
#[primary_span]
pub span: Span,
pub attr_str: &'a str,
pub char_: char,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_alias_start_end)]
pub struct DocAliasStartEnd<'a> {
#[primary_span]
pub span: Span,
pub attr_str: &'a str,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_alias_bad_location)]
pub struct DocAliasBadLocation<'a> {
#[primary_span]
pub span: Span,
pub attr_str: &'a str,
pub location: &'a str,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_alias_not_an_alias)]
pub struct DocAliasNotAnAlias<'a> {
#[primary_span]
pub span: Span,
pub attr_str: &'a str,
}
#[derive(LintDiagnostic)]
#[lint(passes::doc_alias_duplicated)]
pub struct DocAliasDuplicated {
#[label]
pub first_defn: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_alias_not_string_literal)]
pub struct DocAliasNotStringLiteral {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_alias_malformed)]
pub struct DocAliasMalformed {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_keyword_empty_mod)]
pub struct DocKeywordEmptyMod {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_keyword_not_mod)]
pub struct DocKeywordNotMod {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_keyword_invalid_ident)]
pub struct DocKeywordInvalidIdent {
#[primary_span]
pub span: Span,
pub doc_keyword: Symbol,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_tuple_variadic_not_first)]
pub struct DocTupleVariadicNotFirst {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_keyword_only_impl)]
pub struct DocKeywordOnlyImpl {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_inline_conflict)]
#[help]
pub struct DocKeywordConflict {
#[primary_span]
pub spans: MultiSpan,
}
#[derive(LintDiagnostic)]
#[lint(passes::doc_inline_only_use)]
#[note]
pub struct DocInlineOnlyUse {
#[label]
pub attr_span: Span,
#[label(passes::not_a_use_item_label)]
pub item_span: Option<Span>,
}
#[derive(SessionDiagnostic)]
#[error(passes::doc_attr_not_crate_level)]
pub struct DocAttrNotCrateLevel<'a> {
#[primary_span]
pub span: Span,
pub attr_name: &'a str,
}
#[derive(LintDiagnostic)]
#[lint(passes::doc_test_unknown)]
pub struct DocTestUnknown {
pub path: String,
}
#[derive(LintDiagnostic)]
#[lint(passes::doc_test_takes_list)]
pub struct DocTestTakesList;
#[derive(LintDiagnostic)]
#[lint(passes::doc_primitive)]
pub struct DocPrimitive;
#[derive(LintDiagnostic)]
#[lint(passes::doc_test_unknown_any)]
pub struct DocTestUnknownAny {
pub path: String,
}
#[derive(LintDiagnostic)]
#[lint(passes::doc_test_unknown_spotlight)]
#[note]
#[note(passes::no_op_note)]
pub struct DocTestUnknownSpotlight {
pub path: String,
#[suggestion_short(applicability = "machine-applicable", code = "notable_trait")]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[lint(passes::doc_test_unknown_include)]
pub struct DocTestUnknownInclude {
pub path: String,
pub value: String,
pub inner: &'static str,
#[suggestion(code = "#{inner}[doc = include_str!(\"{value}\")]")]
pub sugg: (Span, Applicability),
}
#[derive(LintDiagnostic)]
#[lint(passes::doc_invalid)]
pub struct DocInvalid;
#[derive(SessionDiagnostic)]
#[error(passes::pass_by_value)]
pub struct PassByValue {
#[primary_span]
pub attr_span: Span,
#[label]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::allow_incoherent_impl)]
pub struct AllowIncoherentImpl {
#[primary_span]
pub attr_span: Span,
#[label]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(passes::has_incoherent_inherent_impl)]
pub struct HasIncoherentInherentImpl {
#[primary_span]
pub attr_span: Span,
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[lint(passes::must_use_async)]
pub struct MustUseAsync {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[lint(passes::must_use_no_effect)]
pub struct MustUseNoEffect {
pub article: &'static str,
pub target: rustc_hir::Target,
}
#[derive(SessionDiagnostic)]
#[error(passes::must_not_suspend)]
pub struct MustNotSuspend {
#[primary_span]
pub attr_span: Span,
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[lint(passes::cold)]
#[warn_]
pub struct Cold {
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[lint(passes::link)]
#[warn_]
pub struct Link {
#[label]
pub span: Option<Span>,
}

View file

@ -7,8 +7,8 @@
#![allow(rustc::potential_query_instability)] #![allow(rustc::potential_query_instability)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(iter_intersperse)] #![feature(iter_intersperse)]
#![feature(let_else)]
#![feature(let_chains)] #![feature(let_chains)]
#![feature(let_else)]
#![feature(map_try_insert)] #![feature(map_try_insert)]
#![feature(min_specialization)] #![feature(min_specialization)]
#![feature(try_blocks)] #![feature(try_blocks)]
@ -27,6 +27,7 @@ pub mod dead;
mod debugger_visualizer; mod debugger_visualizer;
mod diagnostic_items; mod diagnostic_items;
pub mod entry; pub mod entry;
mod errors;
pub mod hir_id_validator; pub mod hir_id_validator;
pub mod hir_stats; pub mod hir_stats;
mod lang_items; mod lang_items;

View file

@ -1,4 +1,4 @@
use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic};
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
#[derive(SessionDiagnostic)] #[derive(SessionDiagnostic)]
@ -73,3 +73,19 @@ pub struct InPublicInterface<'a> {
#[label(privacy::visibility_label)] #[label(privacy::visibility_label)]
pub vis_span: Span, pub vis_span: Span,
} }
#[derive(LintDiagnostic)]
#[lint(privacy::from_private_dep_in_public_interface)]
pub struct FromPrivateDependencyInPublicInterface<'a> {
pub kind: &'a str,
pub descr: String,
pub krate: Symbol,
}
#[derive(LintDiagnostic)]
#[lint(privacy::private_in_public_lint)]
pub struct PrivateInPublicLint<'a> {
pub vis_descr: &'static str,
pub kind: &'a str,
pub descr: String,
}

View file

@ -38,8 +38,8 @@ use std::ops::ControlFlow;
use std::{cmp, fmt, mem}; use std::{cmp, fmt, mem};
use errors::{ use errors::{
FieldIsPrivate, FieldIsPrivateLabel, InPublicInterface, InPublicInterfaceTraits, ItemIsPrivate, FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface,
UnnamedItemIsPrivate, InPublicInterfaceTraits, ItemIsPrivate, PrivateInPublicLint, UnnamedItemIsPrivate,
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -1716,19 +1716,14 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
if self.leaks_private_dep(def_id) { if self.leaks_private_dep(def_id) {
self.tcx.struct_span_lint_hir( self.tcx.emit_spanned_lint(
lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES, lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES,
self.tcx.hir().local_def_id_to_hir_id(self.item_def_id), self.tcx.hir().local_def_id_to_hir_id(self.item_def_id),
self.tcx.def_span(self.item_def_id.to_def_id()), self.tcx.def_span(self.item_def_id.to_def_id()),
|lint| { FromPrivateDependencyInPublicInterface {
lint.build(&format!( kind,
"{} `{}` from private dependency '{}' in public \ descr: descr.to_string(),
interface", krate: self.tcx.crate_name(def_id.krate),
kind,
descr,
self.tcx.crate_name(def_id.krate)
))
.emit();
}, },
); );
} }
@ -1754,12 +1749,14 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
} }
}; };
let span = self.tcx.def_span(self.item_def_id.to_def_id()); let span = self.tcx.def_span(self.item_def_id.to_def_id());
let descr = descr.to_string();
if self.has_old_errors if self.has_old_errors
|| self.in_assoc_ty || self.in_assoc_ty
|| self.tcx.resolutions(()).has_pub_restricted || self.tcx.resolutions(()).has_pub_restricted
{ {
let descr = descr.to_string(); let descr = descr.to_string();
let vis_span = self.tcx.def_span(def_id); let vis_span =
self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id));
if kind == "trait" { if kind == "trait" {
self.tcx.sess.emit_err(InPublicInterfaceTraits { self.tcx.sess.emit_err(InPublicInterfaceTraits {
span, span,
@ -1778,19 +1775,11 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
}); });
} }
} else { } else {
let err_code = if kind == "trait" { "E0445" } else { "E0446" }; self.tcx.emit_spanned_lint(
self.tcx.struct_span_lint_hir(
lint::builtin::PRIVATE_IN_PUBLIC, lint::builtin::PRIVATE_IN_PUBLIC,
hir_id, hir_id,
span, span,
|lint| { PrivateInPublicLint { vis_descr, kind, descr },
lint.build(&format!(
"{} (error {})",
format!("{} {} `{}` in public interface", vis_descr, kind, descr),
err_code
))
.emit();
},
); );
} }
} }

View file

@ -1,11 +1,12 @@
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lock; use rustc_data_structures::sync::Lock;
use rustc_span::Symbol;
use rustc_target::abi::{Align, Size}; use rustc_target::abi::{Align, Size};
use std::cmp::{self, Ordering}; use std::cmp::{self, Ordering};
#[derive(Clone, PartialEq, Eq, Hash, Debug)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct VariantInfo { pub struct VariantInfo {
pub name: Option<String>, pub name: Option<Symbol>,
pub kind: SizeKind, pub kind: SizeKind,
pub size: u64, pub size: u64,
pub align: u64, pub align: u64,
@ -20,7 +21,7 @@ pub enum SizeKind {
#[derive(Clone, PartialEq, Eq, Hash, Debug)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct FieldInfo { pub struct FieldInfo {
pub name: String, pub name: Symbol,
pub offset: u64, pub offset: u64,
pub size: u64, pub size: u64,
pub align: u64, pub align: u64,
@ -119,7 +120,7 @@ impl CodeStats {
let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info; let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
let indent = if !struct_like { let indent = if !struct_like {
let name = match name.as_ref() { let name = match name.as_ref() {
Some(name) => name.to_owned(), Some(name) => name.to_string(),
None => i.to_string(), None => i.to_string(),
}; };
println!( println!(

View file

@ -23,7 +23,7 @@ use rustc_hir::GenericParam;
use rustc_hir::Item; use rustc_hir::Item;
use rustc_hir::Node; use rustc_hir::Node;
use rustc_infer::infer::error_reporting::same_type_modulo_infer; use rustc_infer::infer::error_reporting::same_type_modulo_infer;
use rustc_infer::traits::{AmbiguousSelection, TraitEngine}; use rustc_infer::traits::TraitEngine;
use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::select::OverflowError;
use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::error::ExpectedFound;
@ -1403,7 +1403,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
fn annotate_source_of_ambiguity( fn annotate_source_of_ambiguity(
&self, &self,
err: &mut Diagnostic, err: &mut Diagnostic,
impls: &[AmbiguousSelection], impls: &[DefId],
predicate: ty::Predicate<'tcx>, predicate: ty::Predicate<'tcx>,
); );
@ -2036,14 +2036,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
); );
match selcx.select_from_obligation(&obligation) { match selcx.select_from_obligation(&obligation) {
Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => { Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => {
if self.is_tainted_by_errors() && subst.is_none() {
// If `subst.is_none()`, then this is probably two param-env
// candidates or impl candidates that are equal modulo lifetimes.
// Therefore, if we've already emitted an error, just skip this
// one, since it's not particularly actionable.
err.cancel();
return;
}
self.annotate_source_of_ambiguity(&mut err, &impls, predicate); self.annotate_source_of_ambiguity(&mut err, &impls, predicate);
} }
_ => { _ => {
@ -2224,35 +2216,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
fn annotate_source_of_ambiguity( fn annotate_source_of_ambiguity(
&self, &self,
err: &mut Diagnostic, err: &mut Diagnostic,
impls: &[AmbiguousSelection], impls: &[DefId],
predicate: ty::Predicate<'tcx>, predicate: ty::Predicate<'tcx>,
) { ) {
let mut spans = vec![]; let mut spans = vec![];
let mut crates = vec![]; let mut crates = vec![];
let mut post = vec![]; let mut post = vec![];
let mut or_where_clause = false; for def_id in impls {
for ambig in impls { match self.tcx.span_of_impl(*def_id) {
match ambig { Ok(span) => spans.push(span),
AmbiguousSelection::Impl(def_id) => match self.tcx.span_of_impl(*def_id) { Err(name) => {
Ok(span) => spans.push(span), crates.push(name);
Err(name) => { if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) {
crates.push(name); post.push(header);
if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) {
post.push(header);
}
} }
},
AmbiguousSelection::ParamEnv(span) => {
or_where_clause = true;
spans.push(*span);
} }
} }
} }
let msg = format!( let msg = format!("multiple `impl`s satisfying `{}` found", predicate);
"multiple `impl`s{} satisfying `{}` found",
if or_where_clause { " or `where` clauses" } else { "" },
predicate
);
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect(); let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect();
crate_names.sort(); crate_names.sort();
crate_names.dedup(); crate_names.dedup();

View file

@ -6,11 +6,9 @@
//! //!
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
use hir::LangItem; use hir::LangItem;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_infer::traits::util::elaborate_predicates_with_span; use rustc_infer::traits::TraitEngine;
use rustc_infer::traits::{AmbiguousSelection, TraitEngine};
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT; use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT;
use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::print::with_no_trimmed_paths;
@ -201,48 +199,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// and report ambiguity. // and report ambiguity.
if i > 1 { if i > 1 {
debug!("multiple matches, ambig"); debug!("multiple matches, ambig");
// Collect a list of (probable) spans that point to a param-env candidate
let tcx = self.infcx.tcx;
let owner = stack.obligation.cause.body_id.owner.to_def_id();
let predicates = tcx.predicates_of(owner).instantiate_identity(tcx);
let param_env_spans: FxHashMap<_, _> = elaborate_predicates_with_span(
tcx,
std::iter::zip(predicates.predicates, predicates.spans),
)
.filter_map(|obligation| {
let kind = obligation.predicate.kind();
if let ty::PredicateKind::Trait(trait_pred) = kind.skip_binder() {
if trait_pred.trait_ref
== ty::TraitRef::identity(tcx, trait_pred.def_id())
.skip_binder()
{
// HACK: Remap the `Self: Trait` predicate that every trait has to a more useful span
Some((
kind.rebind(trait_pred),
tcx.def_span(trait_pred.def_id()),
))
} else {
Some((kind.rebind(trait_pred), obligation.cause.span))
}
} else {
None
}
})
.collect();
return Err(Ambiguous( return Err(Ambiguous(
candidates candidates
.into_iter() .into_iter()
.filter_map(|c| match c.candidate { .filter_map(|c| match c.candidate {
SelectionCandidate::ImplCandidate(def_id) => { SelectionCandidate::ImplCandidate(def_id) => Some(def_id),
Some(AmbiguousSelection::Impl(def_id))
}
SelectionCandidate::ParamCandidate(predicate) => {
Some(AmbiguousSelection::ParamEnv(
*param_env_spans.get(&predicate)?,
))
}
_ => None, _ => None,
}) })
.collect(), .collect(),

View file

@ -2486,7 +2486,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
concrete type's name `{type_name}` instead if you want to \ concrete type's name `{type_name}` instead if you want to \
specify its type parameters" specify its type parameters"
), ),
type_name.to_string(), type_name,
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} }

View file

@ -544,7 +544,7 @@ fn compare_self_type<'tcx>(
if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
err.span_label(span, format!("trait method declared without `{self_descr}`")); err.span_label(span, format!("trait method declared without `{self_descr}`"));
} else { } else {
err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx)); err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
} }
let reported = err.emit(); let reported = err.emit();
return Err(reported); return Err(reported);
@ -564,7 +564,7 @@ fn compare_self_type<'tcx>(
if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
err.span_label(span, format!("`{self_descr}` used in trait")); err.span_label(span, format!("`{self_descr}` used in trait"));
} else { } else {
err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx)); err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
} }
let reported = err.emit(); let reported = err.emit();
return Err(reported); return Err(reported);
@ -803,7 +803,7 @@ fn compare_number_of_method_arguments<'tcx>(
), ),
); );
} else { } else {
err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx)); err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
} }
err.span_label( err.span_label(
impl_span, impl_span,

View file

@ -1856,7 +1856,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let remaining_private_fields_len = remaining_private_fields.len(); let remaining_private_fields_len = remaining_private_fields.len();
let names = match &remaining_private_fields let names = match &remaining_private_fields
.iter() .iter()
.map(|(name, _, _)| name.to_string()) .map(|(name, _, _)| name)
.collect::<Vec<_>>()[..] .collect::<Vec<_>>()[..]
{ {
_ if remaining_private_fields_len > 6 => String::new(), _ if remaining_private_fields_len > 6 => String::new(),

View file

@ -19,6 +19,7 @@ use rustc_middle::ty::print::with_crate_prefix;
use rustc_middle::ty::ToPolyTraitRef; use rustc_middle::ty::ToPolyTraitRef;
use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable}; use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable};
use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Symbol;
use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span};
use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@ -1548,7 +1549,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Option<ObligationCause<'tcx>>, Option<ObligationCause<'tcx>>,
)], )],
) { ) {
let mut derives = Vec::<(String, Span, String)>::new(); let mut derives = Vec::<(String, Span, Symbol)>::new();
let mut traits = Vec::<Span>::new(); let mut traits = Vec::<Span>::new();
for (pred, _, _) in unsatisfied_predicates { for (pred, _, _) in unsatisfied_predicates {
let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue }; let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue };
@ -1581,12 +1582,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
derives.push(( derives.push((
self_name.clone(), self_name.clone(),
self_span, self_span,
parent_diagnostic_name.to_string(), parent_diagnostic_name,
)); ));
} }
} }
} }
derives.push((self_name, self_span, diagnostic_name.to_string())); derives.push((self_name, self_span, diagnostic_name));
} else { } else {
traits.push(self.tcx.def_span(trait_pred.def_id())); traits.push(self.tcx.def_span(trait_pred.def_id()));
} }
@ -1609,7 +1610,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
continue; continue;
} }
} }
derives_grouped.push((self_name, self_span, trait_name)); derives_grouped.push((self_name, self_span, trait_name.to_string()));
} }
let len = traits.len(); let len = traits.len();

View file

@ -25,7 +25,7 @@ can be broken down into several distinct phases:
- regionck: after main is complete, the regionck pass goes over all - regionck: after main is complete, the regionck pass goes over all
types looking for regions and making sure that they did not escape types looking for regions and making sure that they did not escape
into places they are not in scope. This may also influence the into places where they are not in scope. This may also influence the
final assignments of the various region variables if there is some final assignments of the various region variables if there is some
flexibility. flexibility.

View file

@ -17,7 +17,7 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::query::Providers; use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
use rustc_span::Span; use rustc_span::{Span, Symbol};
use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::hash_map::Entry::{Occupied, Vacant};
@ -123,12 +123,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
ty::GenericParamDefKind::Type { .. } => { ty::GenericParamDefKind::Type { .. } => {
let param_ty = ty::ParamTy::for_def(param); let param_ty = ty::ParamTy::for_def(param);
if !input_parameters.contains(&cgp::Parameter::from(param_ty)) { if !input_parameters.contains(&cgp::Parameter::from(param_ty)) {
report_unused_parameter( report_unused_parameter(tcx, tcx.def_span(param.def_id), "type", param_ty.name);
tcx,
tcx.def_span(param.def_id),
"type",
&param_ty.to_string(),
);
} }
} }
ty::GenericParamDefKind::Lifetime => { ty::GenericParamDefKind::Lifetime => {
@ -140,7 +135,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
tcx, tcx,
tcx.def_span(param.def_id), tcx.def_span(param.def_id),
"lifetime", "lifetime",
&param.name.to_string(), param.name,
); );
} }
} }
@ -151,7 +146,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
tcx, tcx,
tcx.def_span(param.def_id), tcx.def_span(param.def_id),
"const", "const",
&param_ct.to_string(), param_ct.name,
); );
} }
} }
@ -178,7 +173,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
// used elsewhere are not projected back out. // used elsewhere are not projected back out.
} }
fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) { fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol) {
let mut err = struct_span_err!( let mut err = struct_span_err!(
tcx.sess, tcx.sess,
span, span,

View file

@ -836,14 +836,14 @@ impl<T: Clone, V: Borrow<[T]>> Join<&[T]> for [V] {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
impl<T> Borrow<[T]> for Vec<T> { impl<T, A: Allocator> Borrow<[T]> for Vec<T, A> {
fn borrow(&self) -> &[T] { fn borrow(&self) -> &[T] {
&self[..] &self[..]
} }
} }
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
impl<T> BorrowMut<[T]> for Vec<T> { impl<T, A: Allocator> BorrowMut<[T]> for Vec<T, A> {
fn borrow_mut(&mut self) -> &mut [T] { fn borrow_mut(&mut self) -> &mut [T] {
&mut self[..] &mut self[..]
} }

View file

@ -2120,8 +2120,9 @@ fn clean_use_statement<'tcx>(
// forcefully don't inline if this is not public or if the // forcefully don't inline if this is not public or if the
// #[doc(no_inline)] attribute is present. // #[doc(no_inline)] attribute is present.
// Don't inline doc(hidden) imports so they can be stripped at a later stage. // Don't inline doc(hidden) imports so they can be stripped at a later stage.
let mut denied = !(visibility.is_public() let mut denied = cx.output_format.is_json()
|| (cx.render_options.document_private && is_visible_from_parent_mod)) || !(visibility.is_public()
|| (cx.render_options.document_private && is_visible_from_parent_mod))
|| pub_underscore || pub_underscore
|| attrs.iter().any(|a| { || attrs.iter().any(|a| {
a.has_name(sym::doc) a.has_name(sym::doc)

View file

@ -81,6 +81,8 @@ pub(crate) struct DocContext<'tcx> {
pub(crate) inlined: FxHashSet<ItemId>, pub(crate) inlined: FxHashSet<ItemId>,
/// Used by `calculate_doc_coverage`. /// Used by `calculate_doc_coverage`.
pub(crate) output_format: OutputFormat, pub(crate) output_format: OutputFormat,
/// Used by `strip_private`.
pub(crate) show_coverage: bool,
} }
impl<'tcx> DocContext<'tcx> { impl<'tcx> DocContext<'tcx> {
@ -381,6 +383,7 @@ pub(crate) fn run_global_ctxt(
inlined: FxHashSet::default(), inlined: FxHashSet::default(),
output_format, output_format,
render_options, render_options,
show_coverage,
}; };
// Small hack to force the Sized trait to be present. // Small hack to force the Sized trait to be present.

View file

@ -43,7 +43,16 @@ impl JsonRenderer<'_> {
let span = item.span(self.tcx); let span = item.span(self.tcx);
let clean::Item { name, attrs: _, kind: _, visibility, item_id, cfg: _ } = item; let clean::Item { name, attrs: _, kind: _, visibility, item_id, cfg: _ } = item;
let inner = match *item.kind { let inner = match *item.kind {
clean::StrippedItem(_) | clean::KeywordItem(_) => return None, clean::KeywordItem(_) => return None,
clean::StrippedItem(ref inner) => {
match &**inner {
// We document non-empty stripped modules as with `Module::is_stripped` set to
// `true`, to prevent contained items from being orphaned for downstream users,
// as JSON does no inlining.
clean::ModuleItem(m) if !m.items.is_empty() => from_clean_item(item, self.tcx),
_ => return None,
}
}
_ => from_clean_item(item, self.tcx), _ => from_clean_item(item, self.tcx),
}; };
Some(Item { Some(Item {
@ -220,7 +229,9 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
let header = item.fn_header(tcx); let header = item.fn_header(tcx);
match *item.kind { match *item.kind {
ModuleItem(m) => ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx) }), ModuleItem(m) => {
ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx), is_stripped: false })
}
ImportItem(i) => ItemEnum::Import(i.into_tcx(tcx)), ImportItem(i) => ItemEnum::Import(i.into_tcx(tcx)),
StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)), StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)),
UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)), UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)),
@ -257,8 +268,19 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(), bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(),
default: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)), default: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
}, },
// `convert_item` early returns `None` for striped items and keywords. // `convert_item` early returns `None` for stripped items and keywords.
StrippedItem(_) | KeywordItem(_) => unreachable!(), KeywordItem(_) => unreachable!(),
StrippedItem(inner) => {
match *inner {
ModuleItem(m) => ItemEnum::Module(Module {
is_crate,
items: ids(m.items, tcx),
is_stripped: true,
}),
// `convert_item` early returns `None` for stripped items we're not including
_ => unreachable!(),
}
}
ExternCrateItem { ref src } => ItemEnum::ExternCrate { ExternCrateItem { ref src } => ItemEnum::ExternCrate {
name: name.as_ref().unwrap().to_string(), name: name.as_ref().unwrap().to_string(),
rename: src.map(|x| x.to_string()), rename: src.map(|x| x.to_string()),

View file

@ -21,6 +21,7 @@ use rustc_span::def_id::LOCAL_CRATE;
use rustdoc_json_types as types; use rustdoc_json_types as types;
use crate::clean::types::{ExternalCrate, ExternalLocation}; use crate::clean::types::{ExternalCrate, ExternalLocation};
use crate::clean::ItemKind;
use crate::config::RenderOptions; use crate::config::RenderOptions;
use crate::docfs::PathError; use crate::docfs::PathError;
use crate::error::Error; use crate::error::Error;
@ -175,6 +176,14 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
/// the hashmap because certain items (traits and types) need to have their mappings for trait /// the hashmap because certain items (traits and types) need to have their mappings for trait
/// implementations filled out before they're inserted. /// implementations filled out before they're inserted.
fn item(&mut self, item: clean::Item) -> Result<(), Error> { fn item(&mut self, item: clean::Item) -> Result<(), Error> {
trace!("rendering {} {:?}", item.type_(), item.name);
// Flatten items that recursively store other items. We include orphaned items from
// stripped modules and etc that are otherwise reachable.
if let ItemKind::StrippedItem(inner) = &*item.kind {
inner.inner_items().for_each(|i| self.item(i.clone()).unwrap());
}
// Flatten items that recursively store other items // Flatten items that recursively store other items
item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap()); item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap());

View file

@ -24,6 +24,7 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) ->
retained: &mut retained, retained: &mut retained,
access_levels: &cx.cache.access_levels, access_levels: &cx.cache.access_levels,
update_retained: true, update_retained: true,
is_json_output: cx.output_format.is_json() && !cx.show_coverage,
}; };
krate = ImportStripper.fold_crate(stripper.fold_crate(krate)); krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
} }

View file

@ -3,7 +3,7 @@ use rustc_hir::def_id::DefId;
use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::middle::privacy::AccessLevels;
use std::mem; use std::mem;
use crate::clean::{self, Item, ItemIdSet}; use crate::clean::{self, Item, ItemId, ItemIdSet};
use crate::fold::{strip_item, DocFolder}; use crate::fold::{strip_item, DocFolder};
use crate::formats::cache::Cache; use crate::formats::cache::Cache;
@ -11,6 +11,21 @@ pub(crate) struct Stripper<'a> {
pub(crate) retained: &'a mut ItemIdSet, pub(crate) retained: &'a mut ItemIdSet,
pub(crate) access_levels: &'a AccessLevels<DefId>, pub(crate) access_levels: &'a AccessLevels<DefId>,
pub(crate) update_retained: bool, pub(crate) update_retained: bool,
pub(crate) is_json_output: bool,
}
impl<'a> Stripper<'a> {
// We need to handle this differently for the JSON output because some non exported items could
// be used in public API. And so, we need these items as well. `is_exported` only checks if they
// are in the public API, which is not enough.
#[inline]
fn is_item_reachable(&self, item_id: ItemId) -> bool {
if self.is_json_output {
self.access_levels.is_reachable(item_id.expect_def_id())
} else {
self.access_levels.is_exported(item_id.expect_def_id())
}
}
} }
impl<'a> DocFolder for Stripper<'a> { impl<'a> DocFolder for Stripper<'a> {
@ -45,9 +60,8 @@ impl<'a> DocFolder for Stripper<'a> {
| clean::TraitAliasItem(..) | clean::TraitAliasItem(..)
| clean::MacroItem(..) | clean::MacroItem(..)
| clean::ForeignTypeItem => { | clean::ForeignTypeItem => {
if i.item_id.is_local() let item_id = i.item_id;
&& !self.access_levels.is_exported(i.item_id.expect_def_id()) if item_id.is_local() && !self.is_item_reachable(item_id) {
{
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name); debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
return None; return None;
} }

View file

@ -190,6 +190,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
) -> bool { ) -> bool {
debug!("maybe_inline_local res: {:?}", res); debug!("maybe_inline_local res: {:?}", res);
if self.cx.output_format.is_json() {
return false;
}
let tcx = self.cx.tcx; let tcx = self.cx.tcx;
let Some(res_did) = res.opt_def_id() else { let Some(res_did) = res.opt_def_id() else {
return false; return false;

View file

@ -9,7 +9,7 @@ use std::path::PathBuf;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// rustdoc format-version. /// rustdoc format-version.
pub const FORMAT_VERSION: u32 = 15; pub const FORMAT_VERSION: u32 = 16;
/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
/// about the language items in the local crate, as well as info about external items to allow /// about the language items in the local crate, as well as info about external items to allow
@ -245,6 +245,9 @@ pub enum ItemEnum {
pub struct Module { pub struct Module {
pub is_crate: bool, pub is_crate: bool,
pub items: Vec<Id>, pub items: Vec<Id>,
/// If `true`, this module is not part of the public API, but it contains
/// items that are re-exported as public API.
pub is_stripped: bool,
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]

View file

@ -3,6 +3,8 @@
goto: file://|DOC_PATH|/test_docs/index.html goto: file://|DOC_PATH|/test_docs/index.html
// First, we check that the search results are hidden when the Escape key is pressed. // First, we check that the search results are hidden when the Escape key is pressed.
write: (".search-input", "test") write: (".search-input", "test")
// To be SURE that the search will be run.
press-key: 'Enter'
wait-for: "#search h1" // The search element is empty before the first search wait-for: "#search h1" // The search element is empty before the first search
// Check that the currently displayed element is search. // Check that the currently displayed element is search.
wait-for: "#alternative-display #search" wait-for: "#alternative-display #search"

View file

@ -2,6 +2,8 @@
goto: file://|DOC_PATH|/test_docs/index.html goto: file://|DOC_PATH|/test_docs/index.html
show-text: true show-text: true
write: (".search-input", "test") write: (".search-input", "test")
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear... // Waiting for the search results to appear...
wait-for: "#titles" wait-for: "#titles"
assert-text: ("#results .externcrate", "test_docs") assert-text: ("#results .externcrate", "test_docs")

View file

@ -7,6 +7,8 @@ reload:
assert-text: ("//*[@id='reexport.TheStdReexport']", "pub use ::std as TheStdReexport;") assert-text: ("//*[@id='reexport.TheStdReexport']", "pub use ::std as TheStdReexport;")
assert-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"}) assert-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"})
write: (".search-input", "TheStdReexport") write: (".search-input", "TheStdReexport")
// To be SURE that the search will be run.
press-key: 'Enter'
wait-for: "//a[@class='result-import']" wait-for: "//a[@class='result-import']"
assert-attribute: ( assert-attribute: (
"//a[@class='result-import']", "//a[@class='result-import']",
@ -18,6 +20,8 @@ click: "//a[@class='result-import']"
wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgb(73, 74, 61)"}) wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgb(73, 74, 61)"})
// We now check that the alias is working as well on the reexport. // We now check that the alias is working as well on the reexport.
// To be SURE that the search will be run.
press-key: 'Enter'
write: (".search-input", "AliasForTheStdReexport") write: (".search-input", "AliasForTheStdReexport")
wait-for: "//a[@class='result-import']" wait-for: "//a[@class='result-import']"
assert-text: ( assert-text: (

View file

@ -89,6 +89,8 @@ show-text: true
// We reload the page so the local storage settings are being used. // We reload the page so the local storage settings are being used.
reload: reload:
write: (".search-input", "thisisanalias") write: (".search-input", "thisisanalias")
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear... // Waiting for the search results to appear...
wait-for: "#titles" wait-for: "#titles"
// Checking that the colors for the alias element are the ones expected. // Checking that the colors for the alias element are the ones expected.

View file

@ -2,6 +2,8 @@
goto: file://|DOC_PATH|/test_docs/index.html goto: file://|DOC_PATH|/test_docs/index.html
size: (900, 1000) size: (900, 1000)
write: (".search-input", "test") write: (".search-input", "test")
// To be SURE that the search will be run.
press-key: 'Enter'
wait-for: "#search-settings" wait-for: "#search-settings"
// The width is returned by "getComputedStyle" which returns the exact number instead of the // The width is returned by "getComputedStyle" which returns the exact number instead of the
// CSS rule which is "50%"... // CSS rule which is "50%"...

View file

@ -1,6 +1,8 @@
// Checks that the "keyword" results have the expected text alongside them. // Checks that the "keyword" results have the expected text alongside them.
goto: file://|DOC_PATH|/test_docs/index.html goto: file://|DOC_PATH|/test_docs/index.html
write: (".search-input", "CookieMonster") write: (".search-input", "CookieMonster")
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear... // Waiting for the search results to appear...
wait-for: "#titles" wait-for: "#titles"
// Note: The two next assert commands could be merged as one but readability would be // Note: The two next assert commands could be merged as one but readability would be

View file

@ -2,6 +2,8 @@
// First, try a search-by-name // First, try a search-by-name
goto: file://|DOC_PATH|/test_docs/index.html goto: file://|DOC_PATH|/test_docs/index.html
write: (".search-input", "Foo") write: (".search-input", "Foo")
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear... // Waiting for the search results to appear...
wait-for: "#titles" wait-for: "#titles"
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
@ -22,6 +24,8 @@ wait-for-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"})
// Now try search-by-return // Now try search-by-return
goto: file://|DOC_PATH|/test_docs/index.html goto: file://|DOC_PATH|/test_docs/index.html
write: (".search-input", "-> String") write: (".search-input", "-> String")
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear... // Waiting for the search results to appear...
wait-for: "#titles" wait-for: "#titles"
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
@ -42,6 +46,8 @@ wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
// Try with a search-by-return with no results // Try with a search-by-return with no results
goto: file://|DOC_PATH|/test_docs/index.html goto: file://|DOC_PATH|/test_docs/index.html
write: (".search-input", "-> Something") write: (".search-input", "-> Something")
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear... // Waiting for the search results to appear...
wait-for: "#titles" wait-for: "#titles"
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
@ -50,6 +56,8 @@ assert-text: ("#titles > button:nth-of-type(1)", "In Function Return Types", STA
// Try with a search-by-parameter // Try with a search-by-parameter
goto: file://|DOC_PATH|/test_docs/index.html goto: file://|DOC_PATH|/test_docs/index.html
write: (".search-input", "usize pattern") write: (".search-input", "usize pattern")
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear... // Waiting for the search results to appear...
wait-for: "#titles" wait-for: "#titles"
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
@ -58,6 +66,8 @@ assert-text: ("#titles > button:nth-of-type(1)", "In Function Parameters", START
// Try with a search-by-parameter-and-return // Try with a search-by-parameter-and-return
goto: file://|DOC_PATH|/test_docs/index.html goto: file://|DOC_PATH|/test_docs/index.html
write: (".search-input", "pattern -> str") write: (".search-input", "pattern -> str")
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear... // Waiting for the search results to appear...
wait-for: "#titles" wait-for: "#titles"
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})

View file

@ -20,6 +20,8 @@ wait-for-css: ("#settings", {"display": "none"})
// Let's click on it when the search results are displayed. // Let's click on it when the search results are displayed.
focus: ".search-input" focus: ".search-input"
write: "test" write: "test"
// To be SURE that the search will be run.
press-key: 'Enter'
wait-for: "#alternative-display #search" wait-for: "#alternative-display #search"
click: "#settings-menu" click: "#settings-menu"
wait-for-css: ("#settings", {"display": "block"}) wait-for-css: ("#settings", {"display": "block"})

View file

@ -0,0 +1,22 @@
// Regression test for <https://github.com/rust-lang/rust/issues/98007>.
#![feature(no_core)]
#![no_core]
mod auto {
mod action_row {
pub struct ActionRowBuilder;
}
#[doc(hidden)]
pub mod builders {
pub use super::action_row::ActionRowBuilder;
}
}
// @count doc_hidden_failure.json "$.index[*][?(@.name=='builders')]" 2
pub use auto::*;
pub mod builders {
pub use crate::auto::builders::*;
}

View file

@ -0,0 +1 @@
pub struct Foo;

View file

@ -3,15 +3,16 @@
#![no_core] #![no_core]
#![feature(no_core)] #![feature(no_core)]
// @!has glob_extern.json "$.index[*][?(@.name=='mod1')]" // @is glob_extern.json "$.index[*][?(@.name=='mod1')].kind" \"module\"
// @is glob_extern.json "$.index[*][?(@.name=='mod1')].inner.is_stripped" "true"
mod mod1 { mod mod1 {
extern "C" { extern "C" {
// @set public_fn_id = - "$.index[*][?(@.name=='public_fn')].id" // @has - "$.index[*][?(@.name=='public_fn')].id"
pub fn public_fn(); pub fn public_fn();
// @!has - "$.index[*][?(@.name=='private_fn')]" // @!has - "$.index[*][?(@.name=='private_fn')]"
fn private_fn(); fn private_fn();
} }
} }
// @has - "$.index[*][?(@.name=='glob_extern')].inner.items[*]" $public_fn_id // @is - "$.index[*][?(@.kind=='import')].inner.glob" true
pub use mod1::*; pub use mod1::*;

View file

@ -3,9 +3,11 @@
#![no_core] #![no_core]
#![feature(no_core)] #![feature(no_core)]
// @!has glob_private.json "$.index[*][?(@.name=='mod1')]" // @is glob_private.json "$.index[*][?(@.name=='mod1')].kind" \"module\"
// @is glob_private.json "$.index[*][?(@.name=='mod1')].inner.is_stripped" "true"
mod mod1 { mod mod1 {
// @!has - "$.index[*][?(@.name=='mod2')]" // @is - "$.index[*][?(@.name=='mod2')].kind" \"module\"
// @is - "$.index[*][?(@.name=='mod2')].inner.is_stripped" "true"
mod mod2 { mod mod2 {
// @set m2pub_id = - "$.index[*][?(@.name=='Mod2Public')].id" // @set m2pub_id = - "$.index[*][?(@.name=='Mod2Public')].id"
pub struct Mod2Public; pub struct Mod2Public;
@ -13,15 +15,18 @@ mod mod1 {
// @!has - "$.index[*][?(@.name=='Mod2Private')]" // @!has - "$.index[*][?(@.name=='Mod2Private')]"
struct Mod2Private; struct Mod2Private;
} }
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='mod2')]"
pub use self::mod2::*; pub use self::mod2::*;
// @set m1pub_id = - "$.index[*][?(@.name=='Mod1Public')].id" // @set m1pub_id = - "$.index[*][?(@.name=='Mod1Public')].id"
pub struct Mod1Public; pub struct Mod1Public;
// @!has - "$.index[*][?(@.name=='Mod1Private')]" // @!has - "$.index[*][?(@.name=='Mod1Private')]"
struct Mod1Private; struct Mod1Private;
} }
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='mod1')]"
pub use mod1::*; pub use mod1::*;
// @has - "$.index[*][?(@.name=='glob_private')].inner.items[*]" $m2pub_id // @has - "$.index[*][?(@.name=='mod2')].inner.items[*]" $m2pub_id
// @has - "$.index[*][?(@.name=='glob_private')].inner.items[*]" $m1pub_id // @has - "$.index[*][?(@.name=='mod1')].inner.items[*]" $m1pub_id

View file

@ -1,15 +1,17 @@
#![feature(no_core)] #![feature(no_core)]
#![no_core] #![no_core]
// @is in_root_and_mod.json "$.index[*][?(@.name=='foo')].kind" \"module\"
// @is in_root_and_mod.json "$.index[*][?(@.name=='foo')].inner.is_stripped" "true"
mod foo { mod foo {
// @set foo_id = in_root_and_mod.json "$.index[*][?(@.name=='Foo')].id" // @has - "$.index[*][?(@.name=='Foo')]"
pub struct Foo; pub struct Foo;
} }
// @has - "$.index[*][?(@.name=='in_root_and_mod')].inner.items[*]" $foo_id // @has - "$.index[*][?(@.kind=='import' && @.inner.source=='foo::Foo')]"
pub use foo::Foo; pub use foo::Foo;
pub mod bar { pub mod bar {
// @has - "$.index[*][?(@.name=='bar')].inner.items[*]" $foo_id // @has - "$.index[*][?(@.kind=='import' && @.inner.source=='crate::foo::Foo')]"
pub use crate::foo::Foo; pub use crate::foo::Foo;
} }

View file

@ -0,0 +1,18 @@
// aux-build:pub-struct.rs
// Test for the ICE in rust/83057
// Am external type re-exported with different attributes shouldn't cause an error
#![no_core]
#![feature(no_core)]
extern crate pub_struct as foo;
#[doc(inline)]
pub use foo::Foo;
pub mod bar {
pub use foo::Foo;
}
// @count private_twice_one_inline.json "$.index[*][?(@.kind=='import')]" 2

View file

@ -0,0 +1,17 @@
// Test for the ICE in rust/83720
// A pub-in-private type re-exported under two different names shouldn't cause an error
#![no_core]
#![feature(no_core)]
// @is private_two_names.json "$.index[*][?(@.name=='style')].kind" \"module\"
// @is private_two_names.json "$.index[*][?(@.name=='style')].inner.is_stripped" "true"
mod style {
// @has - "$.index[*](?(@.name=='Color'))"
pub struct Color;
}
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='Color')]"
pub use style::Color;
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='Colour')]"
pub use style::Color as Colour;

View file

@ -2,13 +2,13 @@
#![no_core] #![no_core]
#![feature(no_core)] #![feature(no_core)]
// @!has rename_private.json "$.index[*][?(@.name=='inner')]"
// @is rename_private.json "$.index[*][?(@.name=='inner')].kind" \"module\"
// @is rename_private.json "$.index[*][?(@.name=='inner')].inner.is_stripped" "true"
mod inner { mod inner {
// @!has - "$.index[*][?(@.name=='Public')]" // @has - "$.index[*][?(@.name=='Public')]"
pub struct Public; pub struct Public;
} }
// @set newname_id = - "$.index[*][?(@.name=='NewName')].id" // @is - "$.index[*][?(@.kind=='import')].inner.name" \"NewName\"
// @is - "$.index[*][?(@.name=='NewName')].kind" \"struct\"
// @has - "$.index[*][?(@.name=='rename_private')].inner.items[*]" $newname_id
pub use inner::Public as NewName; pub use inner::Public as NewName;

View file

@ -1,15 +1,13 @@
// Regression test for https://github.com/rust-lang/rust/issues/97432. // Regression test for <https://github.com/rust-lang/rust/issues/97432>.
#![feature(no_core)] #![feature(no_core)]
#![no_std] #![no_std]
#![no_core] #![no_core]
// @has same_type_reexported_more_than_once.json // @has same_type_reexported_more_than_once.json
// @set trait_id = - "$.index[*][?(@.name=='Trait')].id" // @has - "$.index[*][?(@.name=='Trait')]"
// @has - "$.index[*][?(@.name=='same_type_reexported_more_than_once')].inner.items[*]" $trait_id
pub use inner::Trait; pub use inner::Trait;
// @set reexport_id = - "$.index[*][?(@.name=='Reexport')].id" // @has - "$.index[*].inner[?(@.name=='Reexport')].id"
// @has - "$.index[*][?(@.name=='same_type_reexported_more_than_once')].inner.items[*]" $reexport_id
pub use inner::Trait as Reexport; pub use inner::Trait as Reexport;
mod inner { mod inner {

Some files were not shown because too many files have changed in this diff Show more