mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-25 13:24:22 +00:00
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:
commit
d5e7f4782e
@ -4239,6 +4239,7 @@ dependencies = [
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_lexer",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
|
@ -7,7 +7,7 @@ use rustc_errors::{
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
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_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::traits::ObligationCause;
|
||||
@ -23,7 +23,7 @@ use rustc_middle::ty::{
|
||||
use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
|
||||
use rustc_span::hygiene::DesugaringKind;
|
||||
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::traits::TraitEngineExt as _;
|
||||
|
||||
@ -1227,8 +1227,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
from_closure: false,
|
||||
region_name:
|
||||
RegionName {
|
||||
source:
|
||||
RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
|
||||
source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
|
||||
..
|
||||
},
|
||||
span,
|
||||
@ -1500,7 +1499,70 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
| BorrowExplanation::UsedLaterInLoop(..)
|
||||
| BorrowExplanation::UsedLaterWhenDropped { .. } => {
|
||||
// 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,
|
||||
name: &Option<String>,
|
||||
upvar_span: Span,
|
||||
upvar_name: &str,
|
||||
upvar_name: Symbol,
|
||||
escape_span: Span,
|
||||
) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
|
||||
let tcx = self.infcx.tcx;
|
||||
@ -2093,7 +2155,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
}
|
||||
StorageDeadOrDrop::Destructor(_) => kind,
|
||||
},
|
||||
ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
|
||||
ProjectionElem::OpaqueCast { .. }
|
||||
| ProjectionElem::Field(..)
|
||||
| ProjectionElem::Downcast(..) => {
|
||||
match place_ty.ty.kind() {
|
||||
ty::Adt(def, _) if def.has_dtor(tcx) => {
|
||||
// Report the outermost adt with a destructor
|
||||
|
@ -12,7 +12,7 @@ use rustc_middle::mir::{
|
||||
};
|
||||
use rustc_middle::ty::adjustment::PointerCast;
|
||||
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 crate::region_infer::BlameConstraint;
|
||||
@ -282,7 +282,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
||||
) {
|
||||
if let ConstraintCategory::OpaqueType = category {
|
||||
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!(
|
||||
"you can add a bound to the {}to make it last less than `'static` and match `{}`",
|
||||
|
@ -226,6 +226,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
}
|
||||
ProjectionElem::Downcast(..) if including_downcast.0 => return None,
|
||||
ProjectionElem::Downcast(..) => (),
|
||||
ProjectionElem::OpaqueCast(..) => (),
|
||||
ProjectionElem::Field(field, _ty) => {
|
||||
// FIXME(project-rfc_2229#36): print capture precisely here.
|
||||
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)
|
||||
}
|
||||
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),
|
||||
},
|
||||
};
|
||||
|
@ -169,6 +169,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
..,
|
||||
ProjectionElem::Index(_)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::OpaqueCast { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::Downcast(..),
|
||||
],
|
||||
|
@ -19,8 +19,7 @@ use rustc_middle::ty::subst::InternalSubsts;
|
||||
use rustc_middle::ty::Region;
|
||||
use rustc_middle::ty::TypeVisitor;
|
||||
use rustc_middle::ty::{self, RegionVid, Ty};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::symbol::{kw, sym, Ident};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::borrowck_errors;
|
||||
@ -758,7 +757,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
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() {
|
||||
Some(simple_ident) => format!("argument `{}`", simple_ident),
|
||||
@ -770,7 +769,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
self.infcx.tcx,
|
||||
diag,
|
||||
fn_returns,
|
||||
lifetime,
|
||||
lifetime.to_string(),
|
||||
Some(arg),
|
||||
captures,
|
||||
Some((param.param_ty_span, param.param_ty.to_string())),
|
||||
|
@ -34,13 +34,13 @@ pub(crate) enum RegionNameSource {
|
||||
/// The `'static` region.
|
||||
Static,
|
||||
/// The free region corresponding to the environment of a closure.
|
||||
SynthesizedFreeEnvRegion(Span, String),
|
||||
SynthesizedFreeEnvRegion(Span, &'static str),
|
||||
/// The region corresponding to an argument.
|
||||
AnonRegionFromArgument(RegionNameHighlight),
|
||||
/// The region corresponding to a closure upvar.
|
||||
AnonRegionFromUpvar(Span, String),
|
||||
AnonRegionFromUpvar(Span, Symbol),
|
||||
/// 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.
|
||||
AnonRegionFromYieldTy(Span, String),
|
||||
/// An anonymous region from an async fn.
|
||||
@ -110,7 +110,7 @@ impl RegionName {
|
||||
}
|
||||
RegionNameSource::SynthesizedFreeEnvRegion(span, note) => {
|
||||
diag.span_label(*span, format!("lifetime `{self}` represents this closure's body"));
|
||||
diag.note(note);
|
||||
diag.note(*note);
|
||||
}
|
||||
RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy(
|
||||
span,
|
||||
@ -350,10 +350,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
|
||||
Some(RegionName {
|
||||
name: region_name,
|
||||
source: RegionNameSource::SynthesizedFreeEnvRegion(
|
||||
fn_decl_span,
|
||||
note.to_string(),
|
||||
),
|
||||
source: RegionNameSource::SynthesizedFreeEnvRegion(fn_decl_span, note),
|
||||
})
|
||||
}
|
||||
|
||||
@ -678,7 +675,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
|
||||
Some(RegionName {
|
||||
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 {
|
||||
name: self.synthesize_region_name(),
|
||||
source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description.to_string()),
|
||||
source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1788,6 +1788,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
for (place_base, elem) in place.iter_projections().rev() {
|
||||
match elem {
|
||||
ProjectionElem::Index(_/*operand*/) |
|
||||
ProjectionElem::OpaqueCast(_) |
|
||||
ProjectionElem::ConstantIndex { .. } |
|
||||
// assigning to P[i] requires P to be valid.
|
||||
ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
|
||||
@ -2179,6 +2180,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
| ProjectionElem::Index(..)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::OpaqueCast { .. }
|
||||
| ProjectionElem::Downcast(..) => {
|
||||
let upvar_field_projection = self.is_upvar_field_projection(place);
|
||||
if let Some(field) = upvar_field_projection {
|
||||
|
@ -255,6 +255,7 @@ fn place_components_conflict<'tcx>(
|
||||
| (ProjectionElem::Index { .. }, _, _)
|
||||
| (ProjectionElem::ConstantIndex { .. }, _, _)
|
||||
| (ProjectionElem::Subslice { .. }, _, _)
|
||||
| (ProjectionElem::OpaqueCast { .. }, _, _)
|
||||
| (ProjectionElem::Downcast { .. }, _, _) => {
|
||||
// Recursive case. This can still be disjoint on a
|
||||
// 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");
|
||||
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, _)) => {
|
||||
if f1 == f2 {
|
||||
// same field (e.g., `a.y` vs. `a.y`) - recur.
|
||||
@ -525,6 +537,7 @@ fn place_projection_conflict<'tcx>(
|
||||
| ProjectionElem::Field(..)
|
||||
| ProjectionElem::Index(..)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::OpaqueCast { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::Downcast(..),
|
||||
_,
|
||||
|
@ -81,6 +81,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
|
||||
}
|
||||
ProjectionElem::Downcast(..)
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::OpaqueCast { .. }
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Index(_) => {
|
||||
cursor = cursor_base;
|
||||
|
@ -790,6 +790,19 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||
}
|
||||
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,
|
||||
self.param_env,
|
||||
proj,
|
||||
|this, field, ()| {
|
||||
|this, field, _| {
|
||||
let ty = this.field_ty(tcx, field);
|
||||
self.normalize(ty, locations)
|
||||
},
|
||||
|_, _| unreachable!(),
|
||||
);
|
||||
curr_projected_ty = projected_ty;
|
||||
}
|
||||
@ -2493,6 +2507,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
ProjectionElem::Field(..)
|
||||
| ProjectionElem::Downcast(..)
|
||||
| ProjectionElem::OpaqueCast(..)
|
||||
| ProjectionElem::Index(..)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. } => {
|
||||
|
@ -825,6 +825,7 @@ pub(crate) fn codegen_place<'tcx>(
|
||||
cplace = cplace.place_deref(fx);
|
||||
}
|
||||
}
|
||||
PlaceElem::OpaqueCast(ty) => cplace = cplace.place_opaque_cast(fx, ty),
|
||||
PlaceElem::Field(field, _ty) => {
|
||||
cplace = cplace.place_field(fx, field);
|
||||
}
|
||||
|
@ -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(
|
||||
self,
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
|
@ -411,6 +411,21 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
||||
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) {
|
||||
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, _) => {
|
||||
cg_base.project_field(bx, field.index())
|
||||
}
|
||||
mir::ProjectionElem::OpaqueCast(ty) => cg_base.project_type(bx, ty),
|
||||
mir::ProjectionElem::Index(index) => {
|
||||
let index = &mir::Operand::Copy(mir::Place::from(index));
|
||||
let index = self.codegen_operand(bx, index);
|
||||
|
@ -436,7 +436,7 @@ fn valtree_into_mplace<'tcx>(
|
||||
|
||||
let offset = place_adjusted.layout.fields.offset(i);
|
||||
place
|
||||
.offset(
|
||||
.offset_with_meta(
|
||||
offset,
|
||||
MemPlaceMeta::Meta(Scalar::from_machine_usize(
|
||||
num_elems as u64,
|
||||
|
@ -297,7 +297,7 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn offset(
|
||||
pub fn offset_with_meta(
|
||||
&self,
|
||||
offset: Size,
|
||||
meta: MemPlaceMeta<Tag>,
|
||||
@ -305,7 +305,7 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> {
|
||||
cx: &impl HasDataLayout,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
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) => {
|
||||
assert!(
|
||||
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> {
|
||||
|
@ -163,7 +163,7 @@ impl<Tag: Provenance> MemPlace<Tag> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn offset<'tcx>(
|
||||
pub fn offset_with_meta<'tcx>(
|
||||
self,
|
||||
offset: Size,
|
||||
meta: MemPlaceMeta<Tag>,
|
||||
@ -199,7 +199,7 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn offset(
|
||||
pub fn offset_with_meta(
|
||||
&self,
|
||||
offset: Size,
|
||||
meta: MemPlaceMeta<Tag>,
|
||||
@ -207,12 +207,22 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> {
|
||||
cx: &impl HasDataLayout,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
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),
|
||||
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]
|
||||
pub fn from_aligned_ptr(ptr: Pointer<Option<Tag>>, layout: TyAndLayout<'tcx>) -> Self {
|
||||
MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi }
|
||||
|
@ -63,7 +63,7 @@ where
|
||||
|
||||
// We do not look at `base.layout.align` nor `field_layout.align`, unlike
|
||||
// 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.
|
||||
@ -193,9 +193,7 @@ where
|
||||
let offset = stride * index; // `Size` multiplication
|
||||
// All fields have the same layout.
|
||||
let field_layout = base.layout.field(self, 0);
|
||||
assert!(!field_layout.is_unsized());
|
||||
|
||||
base.offset(offset, MemPlaceMeta::None, field_layout, self)
|
||||
base.offset(offset, field_layout, self)
|
||||
}
|
||||
_ => span_bug!(
|
||||
self.cur_span(),
|
||||
@ -215,10 +213,10 @@ where
|
||||
let abi::FieldsShape::Array { stride, .. } = base.layout.fields else {
|
||||
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;
|
||||
// `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.
|
||||
@ -326,7 +324,7 @@ where
|
||||
}
|
||||
};
|
||||
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(
|
||||
@ -351,6 +349,11 @@ where
|
||||
) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
|
||||
use rustc_middle::mir::ProjectionElem::*;
|
||||
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())?,
|
||||
Downcast(_, variant) => self.place_downcast(base, variant)?,
|
||||
Deref => self.deref_operand(&self.place_to_op(base)?)?.into(),
|
||||
@ -375,6 +378,11 @@ where
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
||||
use rustc_middle::mir::ProjectionElem::*;
|
||||
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())?,
|
||||
Downcast(_, variant) => self.operand_downcast(base, variant)?,
|
||||
Deref => self.deref_operand(base)?.into(),
|
||||
|
@ -853,7 +853,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
||||
self.visit_scalar(scalar, scalar_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
|
||||
// is subtle due to enums having ScalarPair layout, where one field
|
||||
// is the discriminant.
|
||||
@ -867,7 +868,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
||||
}
|
||||
Abi::Vector { .. } => {
|
||||
// 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 { .. } => {
|
||||
// Nothing to do.
|
||||
|
@ -8,23 +8,33 @@ use rustc_target::abi::{FieldsShape, VariantIdx, Variants};
|
||||
|
||||
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.
|
||||
// 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).
|
||||
/// 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,
|
||||
/// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
|
||||
pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy {
|
||||
/// Gets this value's layout.
|
||||
fn layout(&self) -> TyAndLayout<'tcx>;
|
||||
|
||||
/// Makes this into an `OpTy`.
|
||||
fn to_op(&self, ecx: &InterpCx<'mir, 'tcx, M>)
|
||||
-> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>;
|
||||
/// 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: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
||||
self.to_op_for_read(ecx)
|
||||
}
|
||||
|
||||
/// Creates this from an `OpTy`.
|
||||
///
|
||||
/// If `to_op` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
|
||||
fn from_op(mplace: OpTy<'tcx, M::PointerTag>) -> Self;
|
||||
/// 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(
|
||||
@ -41,8 +51,50 @@ pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy {
|
||||
) -> InterpResult<'tcx, Self>;
|
||||
}
|
||||
|
||||
// Operands and memory-places are both values.
|
||||
// Places in general are not due to `place_field` having to do `force_allocation`.
|
||||
/// A thing that we can project into given *mutable* access to `ecx`, and that has a layout.
|
||||
/// 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> {
|
||||
#[inline(always)]
|
||||
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)]
|
||||
fn to_op(
|
||||
fn to_op_for_read(
|
||||
&self,
|
||||
_ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> 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)]
|
||||
fn from_op(op: OpTy<'tcx, M::PointerTag>) -> Self {
|
||||
op
|
||||
fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self {
|
||||
*op
|
||||
}
|
||||
|
||||
#[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>
|
||||
for MPlaceTy<'tcx, M::PointerTag>
|
||||
{
|
||||
@ -90,7 +190,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn to_op(
|
||||
fn to_op_for_read(
|
||||
&self,
|
||||
_ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
||||
@ -98,8 +198,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn from_op(op: OpTy<'tcx, M::PointerTag>) -> Self {
|
||||
// assert is justified because our `to_op` only ever produces `Indirect` operands.
|
||||
fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self {
|
||||
// assert is justified because our `to_op_for_read` only ever produces `Indirect` operands.
|
||||
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 {
|
||||
($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.
|
||||
pub trait $visitor_trait_name<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
|
||||
type V: Value<'mir, 'tcx, M>;
|
||||
pub trait $visitor_trait<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
|
||||
type V: $value_trait<'mir, 'tcx, M>;
|
||||
|
||||
/// The visitor must have an `InterpCx` in it.
|
||||
fn ecx(&$($mutability)? self)
|
||||
@ -215,19 +415,20 @@ macro_rules! make_value_visitor {
|
||||
}
|
||||
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.
|
||||
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.
|
||||
ty::Dynamic(..) => {
|
||||
// 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 inner = self.ecx().unpack_dyn_trait(&dest)?.1;
|
||||
trace!("walk_value: dyn object layout: {:#?}", inner.layout);
|
||||
let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.1;
|
||||
trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
|
||||
// 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
|
||||
// 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.
|
||||
match v.layout().fields {
|
||||
FieldsShape::Primitive => {},
|
||||
FieldsShape::Primitive => {}
|
||||
FieldsShape::Union(fields) => {
|
||||
self.visit_union(v, fields)?;
|
||||
},
|
||||
}
|
||||
FieldsShape::Arbitrary { ref offsets, .. } => {
|
||||
// FIXME: We collect in a vec because otherwise there are lifetime
|
||||
// errors: Projecting to a field needs access to `ecx`.
|
||||
@ -291,16 +492,17 @@ macro_rules! make_value_visitor {
|
||||
})
|
||||
.collect();
|
||||
self.visit_aggregate(v, fields.into_iter())?;
|
||||
},
|
||||
}
|
||||
FieldsShape::Array { .. } => {
|
||||
// Let's get an mplace first.
|
||||
let op = v.to_op(self.ecx())?;
|
||||
// Let's get an mplace (or immediate) first.
|
||||
// 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.
|
||||
// This uses the *run-time length*, i.e., if we are a slice,
|
||||
// the dynamic info from the metadata is used.
|
||||
let iter = self.ecx().operand_array_fields(&op)?
|
||||
.map(|f| f.and_then(|f| {
|
||||
Ok(Value::from_op(f))
|
||||
Ok($value_trait::from_op(&f))
|
||||
}));
|
||||
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
|
||||
// with *its* fields.
|
||||
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 inner = v.project_downcast(self.ecx(), idx)?;
|
||||
trace!("walk_value: variant layout: {:#?}", inner.layout());
|
||||
@ -325,5 +527,5 @@ macro_rules! make_value_visitor {
|
||||
}
|
||||
}
|
||||
|
||||
make_value_visitor!(ValueVisitor,);
|
||||
make_value_visitor!(MutValueVisitor, mut);
|
||||
make_value_visitor!(ValueVisitor, Value,);
|
||||
make_value_visitor!(MutValueVisitor, ValueMut, mut);
|
||||
|
@ -652,6 +652,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
|
||||
ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Downcast(..)
|
||||
| ProjectionElem::OpaqueCast(..)
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::Field(..)
|
||||
| ProjectionElem::Index(_) => {}
|
||||
|
@ -316,6 +316,7 @@ where
|
||||
|
||||
ProjectionElem::Deref
|
||||
| ProjectionElem::Field(_, _)
|
||||
| ProjectionElem::OpaqueCast(_)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::Downcast(_, _)
|
||||
|
@ -361,7 +361,7 @@ impl<'tcx> Validator<'_, 'tcx> {
|
||||
return Err(Unpromotable);
|
||||
}
|
||||
}
|
||||
ProjectionElem::Downcast(..) => {
|
||||
ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => {
|
||||
return Err(Unpromotable);
|
||||
}
|
||||
|
||||
|
151
compiler/rustc_error_messages/locales/en-US/passes.ftl
Normal file
151
compiler/rustc_error_messages/locales/en-US/passes.ftl
Normal 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
|
@ -10,3 +10,12 @@ privacy-unnamed-item-is-private = {$kind} is private
|
||||
privacy-in-public-interface = {$vis_descr} {$kind} `{$descr}` in public interface
|
||||
.label = can't leak {$vis_descr} {$kind}
|
||||
.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
|
||||
})
|
||||
|
@ -37,6 +37,7 @@ fluent_messages! {
|
||||
expand => "../locales/en-US/expand.ftl",
|
||||
lint => "../locales/en-US/lint.ftl",
|
||||
parser => "../locales/en-US/parser.ftl",
|
||||
passes => "../locales/en-US/passes.ftl",
|
||||
privacy => "../locales/en-US/privacy.ftl",
|
||||
typeck => "../locales/en-US/typeck.ftl",
|
||||
}
|
||||
|
@ -40,6 +40,35 @@ pub trait IntoDiagnosticArg {
|
||||
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 {
|
||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||
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> {
|
||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
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()))
|
||||
DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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> {
|
||||
self.to_string().into_diagnostic_arg()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoDiagnosticArg for &'a str {
|
||||
impl IntoDiagnosticArg for String {
|
||||
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
|
||||
}
|
||||
|
||||
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![
|
||||
(format!("`{}` from trait: `", name), Style::NoStyle),
|
||||
(signature, Style::Highlight),
|
||||
|
@ -595,6 +595,7 @@ macro_rules! error_code {
|
||||
pub struct LintDiagnosticBuilder<'a, G: EmissionGuarantee>(DiagnosticBuilder<'a, G>);
|
||||
|
||||
impl<'a, G: EmissionGuarantee> LintDiagnosticBuilder<'a, G> {
|
||||
#[rustc_lint_diagnostics]
|
||||
/// Return the inner `DiagnosticBuilder`, first setting the primary message to `msg`.
|
||||
pub fn build(mut self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'a, G> {
|
||||
self.0.set_primary_message(msg);
|
||||
|
@ -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, mod_name);
|
||||
let default_path_str = format!("{}{}.rs", relative_prefix, ident.name);
|
||||
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 secondary_path = dir_path.join(&secondary_path_str);
|
||||
let default_exists = sess.source_map().file_exists(&default_path);
|
||||
|
@ -138,7 +138,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPr
|
||||
if let TypeVariableOriginKind::TypeParameterDefinition(name, _) =
|
||||
infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind
|
||||
{
|
||||
Some(name.to_string())
|
||||
Some(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -151,7 +151,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPr
|
||||
if let ConstVariableOriginKind::ConstParameterDefinition(name, _) =
|
||||
infcx.inner.borrow_mut().const_unification_table().probe_value(ct_vid).origin.kind
|
||||
{
|
||||
return Some(name.to_string());
|
||||
return Some(name);
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
|
||||
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
||||
}
|
||||
(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")
|
||||
.emit();
|
||||
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
||||
|
@ -8,12 +8,13 @@ use crate::diagnostics::utils::{
|
||||
report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
|
||||
Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
|
||||
};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
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};
|
||||
|
||||
@ -80,8 +81,8 @@ impl DiagnosticDeriveBuilder {
|
||||
}
|
||||
|
||||
pub fn body<'s>(&mut self, structure: &mut Structure<'s>) -> (TokenStream, TokenStream) {
|
||||
// Keep track of which fields are subdiagnostics or have no attributes.
|
||||
let mut subdiagnostics_or_empty = std::collections::HashSet::new();
|
||||
// Keep track of which fields need to be handled with a by-move binding.
|
||||
let mut needs_moved = std::collections::HashSet::new();
|
||||
|
||||
// Generates calls to `span_label` and similar functions based on the attributes
|
||||
// on fields. Code for suggestions uses formatting machinery and the value of
|
||||
@ -92,16 +93,11 @@ impl DiagnosticDeriveBuilder {
|
||||
let attrs = structure
|
||||
.clone()
|
||||
.filter(|field_binding| {
|
||||
let attrs = &field_binding.ast().attrs;
|
||||
|
||||
(!attrs.is_empty()
|
||||
&& attrs.iter().all(|attr| {
|
||||
"subdiagnostic" != attr.path.segments.last().unwrap().ident.to_string()
|
||||
}))
|
||||
|| {
|
||||
subdiagnostics_or_empty.insert(field_binding.binding.clone());
|
||||
false
|
||||
}
|
||||
let ast = &field_binding.ast();
|
||||
!self.needs_move(ast) || {
|
||||
needs_moved.insert(field_binding.binding.clone());
|
||||
false
|
||||
}
|
||||
})
|
||||
.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
|
||||
// argument to the diagnostic so that it can be referred to by Fluent messages.
|
||||
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));
|
||||
|
||||
(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
|
||||
/// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates
|
||||
/// diagnostic builder calls for setting error code and creating note/help messages.
|
||||
@ -131,7 +156,7 @@ impl DiagnosticDeriveBuilder {
|
||||
let name = name.as_str();
|
||||
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 {
|
||||
// Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or
|
||||
@ -139,8 +164,12 @@ impl DiagnosticDeriveBuilder {
|
||||
Meta::List(MetaList { ref nested, .. }) => nested,
|
||||
// Subdiagnostics without spans can be applied to the type too, and these are just
|
||||
// paths: `#[help]` and `#[note]`
|
||||
Meta::Path(_) if is_help_or_note => {
|
||||
let fn_name = proc_macro2::Ident::new(name, attr.span());
|
||||
Meta::Path(_) if is_help_note_or_warn => {
|
||||
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); });
|
||||
}
|
||||
_ => throw_invalid_attr!(attr, &meta),
|
||||
@ -152,9 +181,11 @@ impl DiagnosticDeriveBuilder {
|
||||
"error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)),
|
||||
"warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)),
|
||||
"lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)),
|
||||
"help" | "note" => (),
|
||||
"help" | "note" | "warn_" => (),
|
||||
_ => 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();
|
||||
if let Some(nested_attr) = nested_iter.next() {
|
||||
// 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| {
|
||||
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 {
|
||||
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());
|
||||
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
|
||||
}
|
||||
@ -178,7 +211,7 @@ impl DiagnosticDeriveBuilder {
|
||||
self.slug.set_once((path.clone(), span));
|
||||
}
|
||||
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" =>
|
||||
{
|
||||
// don't error for valid follow-up attributes
|
||||
@ -227,57 +260,55 @@ impl DiagnosticDeriveBuilder {
|
||||
let field = binding_info.ast();
|
||||
let field_binding = &binding_info.binding;
|
||||
|
||||
let inner_ty = FieldInnerTy::from_type(&field.ty);
|
||||
|
||||
// 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() {
|
||||
if self.should_generate_set_arg(&field) {
|
||||
let diag = &self.diag;
|
||||
let ident = field.ident.as_ref().unwrap();
|
||||
quote! {
|
||||
return quote! {
|
||||
#diag.set_arg(
|
||||
stringify!(#ident),
|
||||
#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(
|
||||
@ -324,10 +355,12 @@ impl DiagnosticDeriveBuilder {
|
||||
report_error_if_not_applied_to_span(attr, &info)?;
|
||||
Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label }))
|
||||
}
|
||||
"note" | "help" => {
|
||||
let path = match name {
|
||||
"note" => parse_quote! { _subdiag::note },
|
||||
"help" => parse_quote! { _subdiag::help },
|
||||
"note" | "help" | "warn_" => {
|
||||
let warn_ident = Ident::new("warn", Span::call_site());
|
||||
let (ident, path) = match name {
|
||||
"note" => (ident, parse_quote! { _subdiag::note }),
|
||||
"help" => (ident, parse_quote! { _subdiag::help }),
|
||||
"warn_" => (&warn_ident, parse_quote! { _subdiag::warn }),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
|
||||
@ -364,10 +397,10 @@ impl DiagnosticDeriveBuilder {
|
||||
"suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => {
|
||||
return self.generate_inner_field_code_suggestion(attr, info);
|
||||
}
|
||||
"label" | "help" | "note" => (),
|
||||
"label" | "help" | "note" | "warn_" => (),
|
||||
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
||||
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",
|
||||
)
|
||||
}),
|
||||
@ -396,7 +429,14 @@ impl DiagnosticDeriveBuilder {
|
||||
Ok(self.add_spanned_subdiagnostic(binding, 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!(),
|
||||
}
|
||||
}
|
||||
|
@ -260,10 +260,12 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
|
||||
#generated
|
||||
|
||||
pub mod _subdiag {
|
||||
pub const note: crate::SubdiagnosticMessage =
|
||||
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
|
||||
pub const help: crate::SubdiagnosticMessage =
|
||||
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 =
|
||||
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label"));
|
||||
pub const suggestion: crate::SubdiagnosticMessage =
|
||||
|
@ -37,6 +37,8 @@ enum SubdiagnosticKind {
|
||||
Note,
|
||||
/// `#[help(...)]`
|
||||
Help,
|
||||
/// `#[warn_(...)]`
|
||||
Warn,
|
||||
/// `#[suggestion{,_short,_hidden,_verbose}]`
|
||||
Suggestion(SubdiagnosticSuggestionKind),
|
||||
}
|
||||
@ -49,6 +51,7 @@ impl FromStr for SubdiagnosticKind {
|
||||
"label" => Ok(SubdiagnosticKind::Label),
|
||||
"note" => Ok(SubdiagnosticKind::Note),
|
||||
"help" => Ok(SubdiagnosticKind::Help),
|
||||
"warn_" => Ok(SubdiagnosticKind::Warn),
|
||||
"suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
|
||||
"suggestion_short" => {
|
||||
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
|
||||
@ -70,6 +73,7 @@ impl quote::IdentFragment for SubdiagnosticKind {
|
||||
SubdiagnosticKind::Label => write!(f, "label"),
|
||||
SubdiagnosticKind::Note => write!(f, "note"),
|
||||
SubdiagnosticKind::Help => write!(f, "help"),
|
||||
SubdiagnosticKind::Warn => write!(f, "warn"),
|
||||
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
|
||||
write!(f, "suggestion")
|
||||
}
|
||||
|
@ -85,7 +85,13 @@ pub(crate) fn report_error_if_not_applied_to_span(
|
||||
attr: &Attribute,
|
||||
info: &FieldInfo<'_>,
|
||||
) -> 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.
|
||||
|
@ -130,8 +130,9 @@ decl_derive!(
|
||||
warning,
|
||||
error,
|
||||
lint,
|
||||
note,
|
||||
help,
|
||||
note,
|
||||
warn_,
|
||||
// field attributes
|
||||
skip_arg,
|
||||
primary_span,
|
||||
@ -148,8 +149,9 @@ decl_derive!(
|
||||
warning,
|
||||
error,
|
||||
lint,
|
||||
note,
|
||||
help,
|
||||
note,
|
||||
warn_,
|
||||
// field attributes
|
||||
skip_arg,
|
||||
primary_span,
|
||||
@ -166,6 +168,7 @@ decl_derive!(
|
||||
label,
|
||||
help,
|
||||
note,
|
||||
warn_,
|
||||
suggestion,
|
||||
suggestion_short,
|
||||
suggestion_hidden,
|
||||
|
@ -1397,6 +1397,7 @@ impl<V, T> ProjectionElem<V, T> {
|
||||
|
||||
Self::Field(_, _)
|
||||
| Self::Index(_)
|
||||
| Self::OpaqueCast(_)
|
||||
| Self::ConstantIndex { .. }
|
||||
| Self::Subslice { .. }
|
||||
| Self::Downcast(_, _) => false,
|
||||
@ -1574,7 +1575,9 @@ impl Debug for Place<'_> {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
for elem in self.projection.iter().rev() {
|
||||
match elem {
|
||||
ProjectionElem::Downcast(_, _) | ProjectionElem::Field(_, _) => {
|
||||
ProjectionElem::OpaqueCast(_)
|
||||
| ProjectionElem::Downcast(_, _)
|
||||
| ProjectionElem::Field(_, _) => {
|
||||
write!(fmt, "(").unwrap();
|
||||
}
|
||||
ProjectionElem::Deref => {
|
||||
@ -1590,6 +1593,9 @@ impl Debug for Place<'_> {
|
||||
|
||||
for elem in self.projection.iter() {
|
||||
match elem {
|
||||
ProjectionElem::OpaqueCast(ty) => {
|
||||
write!(fmt, " as {})", ty)?;
|
||||
}
|
||||
ProjectionElem::Downcast(Some(name), _index) => {
|
||||
write!(fmt, " as {})", name)?;
|
||||
}
|
||||
|
@ -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
|
||||
/// 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.
|
||||
/// - [`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
|
||||
/// 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
|
||||
@ -856,6 +859,10 @@ pub enum ProjectionElem<V, T> {
|
||||
///
|
||||
/// The included Symbol is the name of the variant, used for printing MIR.
|
||||
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
|
||||
|
@ -57,7 +57,7 @@ impl<'tcx> PlaceTy<'tcx> {
|
||||
/// `PlaceElem`, where we can just use the `Ty` that is already
|
||||
/// stored inline on field projection elems.
|
||||
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, |...| { ... })`
|
||||
@ -71,6 +71,7 @@ impl<'tcx> PlaceTy<'tcx> {
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
elem: &ProjectionElem<V, T>,
|
||||
mut handle_field: impl FnMut(&Self, Field, T) -> Ty<'tcx>,
|
||||
mut handle_opaque_cast: impl FnMut(&Self, T) -> Ty<'tcx>,
|
||||
) -> PlaceTy<'tcx>
|
||||
where
|
||||
V: ::std::fmt::Debug,
|
||||
@ -109,6 +110,7 @@ impl<'tcx> PlaceTy<'tcx> {
|
||||
PlaceTy { ty: self.ty, variant_index: Some(index) }
|
||||
}
|
||||
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);
|
||||
answer
|
||||
|
@ -182,6 +182,7 @@ impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> {
|
||||
Ok(match self {
|
||||
Deref => Deref,
|
||||
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)?),
|
||||
Downcast(symbol, variantidx) => Downcast(symbol, variantidx),
|
||||
ConstantIndex { offset, min_length, from_end } => {
|
||||
|
@ -1064,6 +1064,11 @@ macro_rules! visit_place_fns {
|
||||
self.visit_ty(&mut new_ty, TyContext::Location(location));
|
||||
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::ConstantIndex { .. }
|
||||
| PlaceElem::Subslice { .. }
|
||||
@ -1133,7 +1138,7 @@ macro_rules! visit_place_fns {
|
||||
location: Location,
|
||||
) {
|
||||
match elem {
|
||||
ProjectionElem::Field(_field, ty) => {
|
||||
ProjectionElem::OpaqueCast(ty) | ProjectionElem::Field(_, ty) => {
|
||||
self.visit_ty(ty, TyContext::Location(location));
|
||||
}
|
||||
ProjectionElem::Index(local) => {
|
||||
|
@ -542,17 +542,9 @@ pub enum SelectionError<'tcx> {
|
||||
ErrorReporting,
|
||||
/// Multiple applicable `impl`s where found. The `DefId`s correspond to
|
||||
/// 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
|
||||
/// can be one of three outcomes:
|
||||
///
|
||||
|
@ -1975,7 +1975,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||
min_size = field_end;
|
||||
}
|
||||
FieldInfo {
|
||||
name: name.to_string(),
|
||||
name,
|
||||
offset: offset.bytes(),
|
||||
size: field_layout.size.bytes(),
|
||||
align: field_layout.align.abi.bytes(),
|
||||
@ -1984,7 +1984,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||
.collect();
|
||||
|
||||
VariantInfo {
|
||||
name: n.map(|n| n.to_string()),
|
||||
name: n,
|
||||
kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact },
|
||||
align: layout.align.abi.bytes(),
|
||||
size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() },
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
fn const_infer_name(&self, _: ty::ConstVid<'tcx>) -> Option<String> {
|
||||
fn const_infer_name(&self, _: ty::ConstVid<'tcx>) -> Option<Symbol> {
|
||||
None
|
||||
}
|
||||
|
||||
@ -1550,8 +1550,8 @@ pub struct FmtPrinterData<'a, 'tcx> {
|
||||
|
||||
pub region_highlight_mode: RegionHighlightMode<'tcx>,
|
||||
|
||||
pub ty_infer_name_resolver: Option<Box<dyn Fn(ty::TyVid) -> Option<String> + 'a>>,
|
||||
pub const_infer_name_resolver: Option<Box<dyn Fn(ty::ConstVid<'tcx>) -> 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<Symbol> + 'a>>,
|
||||
}
|
||||
|
||||
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> {
|
||||
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))
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::hir::place::Projection as HirProjection;
|
||||
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::tcx::PlaceTy;
|
||||
use rustc_middle::mir::AssertKind::BoundsCheck;
|
||||
use rustc_middle::mir::*;
|
||||
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`
|
||||
/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(crate) struct PlaceBuilder<'tcx> {
|
||||
pub(in crate::build) struct PlaceBuilder<'tcx> {
|
||||
base: PlaceBase,
|
||||
projection: Vec<PlaceElem<'tcx>>,
|
||||
}
|
||||
@ -104,6 +105,8 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
|
||||
variant = Some(*idx);
|
||||
continue;
|
||||
}
|
||||
// These do not affect anything, they just make sure we know the right type.
|
||||
ProjectionElem::OpaqueCast(_) => continue,
|
||||
ProjectionElem::Index(..)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. } => {
|
||||
@ -201,10 +204,10 @@ fn find_capture_matching_projections<'a, 'tcx>(
|
||||
/// `PlaceBuilder` now starts from `PlaceBase::Local`.
|
||||
///
|
||||
/// 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>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
cx: &Builder<'_, 'tcx>,
|
||||
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
|
||||
match from_builder.base {
|
||||
PlaceBase::Local(_) => Ok(from_builder),
|
||||
@ -219,13 +222,13 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
|
||||
|
||||
let Some((capture_index, capture)) =
|
||||
find_capture_matching_projections(
|
||||
typeck_results,
|
||||
cx.typeck_results,
|
||||
var_hir_id,
|
||||
closure_def_id,
|
||||
&from_builder.projection,
|
||||
) else {
|
||||
let closure_span = tcx.def_span(closure_def_id);
|
||||
if !enable_precise_capture(tcx, closure_span) {
|
||||
let closure_span = cx.tcx.def_span(closure_def_id);
|
||||
if !enable_precise_capture(cx.tcx, closure_span) {
|
||||
bug!(
|
||||
"No associated capture found for {:?}[{:#?}] even though \
|
||||
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
|
||||
let closure_hir_id = 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_hir_id = cx.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local());
|
||||
let closure_ty = cx.typeck_results.node_type(closure_hir_id);
|
||||
|
||||
let substs = match closure_ty.kind() {
|
||||
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,
|
||||
// now we apply the remaining to the upvar resolved place.
|
||||
trace!(?capture.place, ?from_builder.projection);
|
||||
let remaining_projections = strip_prefix(
|
||||
capture.place.base_ty,
|
||||
from_builder.projection,
|
||||
&capture.place.projections,
|
||||
);
|
||||
upvar_resolved_place_builder.projection.extend(remaining_projections);
|
||||
trace!(?upvar_resolved_place_builder);
|
||||
|
||||
Ok(upvar_resolved_place_builder)
|
||||
}
|
||||
@ -294,16 +299,21 @@ fn strip_prefix<'tcx>(
|
||||
prefix_projections: &[HirProjection<'tcx>],
|
||||
) -> impl Iterator<Item = PlaceElem<'tcx>> {
|
||||
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 {
|
||||
match projection.kind {
|
||||
HirProjectionKind::Deref => {
|
||||
assert!(matches!(iter.next(), Some(ProjectionElem::Deref)));
|
||||
assert!(matches!(next(), Some(ProjectionElem::Deref)));
|
||||
}
|
||||
HirProjectionKind::Field(..) => {
|
||||
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 => {
|
||||
bug!("unexpected projection kind: {:?}", projection);
|
||||
@ -315,24 +325,32 @@ fn strip_prefix<'tcx>(
|
||||
}
|
||||
|
||||
impl<'tcx> PlaceBuilder<'tcx> {
|
||||
pub(crate) fn into_place<'a>(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
) -> Place<'tcx> {
|
||||
pub(crate) fn into_place(self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> {
|
||||
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 {
|
||||
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>(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
) -> PlaceBuilder<'tcx> {
|
||||
to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap()
|
||||
fn expect_upvars_resolved(self, cx: &Builder<'_, 'tcx>) -> PlaceBuilder<'tcx> {
|
||||
to_upvars_resolved_place_builder(self, cx).unwrap()
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// generated doesn't require a read for this place. Failures will only
|
||||
/// happen inside closures.
|
||||
pub(crate) fn try_upvars_resolved<'a>(
|
||||
pub(crate) fn try_upvars_resolved(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
cx: &Builder<'_, '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 {
|
||||
@ -411,7 +428,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
expr: &Expr<'tcx>,
|
||||
) -> BlockAnd<Place<'tcx>> {
|
||||
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
|
||||
@ -435,7 +452,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
expr: &Expr<'tcx>,
|
||||
) -> BlockAnd<Place<'tcx>> {
|
||||
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
|
||||
@ -530,7 +547,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
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(
|
||||
block,
|
||||
Statement {
|
||||
@ -682,7 +699,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
if is_outermost_index {
|
||||
self.read_fake_borrows(block, fake_borrow_temps, source_info)
|
||||
} 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(
|
||||
&base_place,
|
||||
block,
|
||||
@ -710,12 +727,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
let lt = self.temp(bool_ty, expr_span);
|
||||
|
||||
// len = len(slice)
|
||||
self.cfg.push_assign(
|
||||
block,
|
||||
source_info,
|
||||
len,
|
||||
Rvalue::Len(slice.into_place(self.tcx, self.typeck_results)),
|
||||
);
|
||||
self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.into_place(self)));
|
||||
// lt = idx < len
|
||||
self.cfg.push_assign(
|
||||
block,
|
||||
@ -795,6 +807,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
ProjectionElem::Field(..)
|
||||
| ProjectionElem::Downcast(..)
|
||||
| ProjectionElem::OpaqueCast(..)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. } => (),
|
||||
}
|
||||
|
@ -321,11 +321,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
let place_builder =
|
||||
unpack!(block = this.as_place_builder(block, &this.thir[*thir_place]));
|
||||
|
||||
if let Ok(place_builder_resolved) =
|
||||
place_builder.try_upvars_resolved(this.tcx, this.typeck_results)
|
||||
{
|
||||
let mir_place =
|
||||
place_builder_resolved.into_place(this.tcx, this.typeck_results);
|
||||
if let Ok(place_builder_resolved) = place_builder.try_upvars_resolved(this) {
|
||||
let mir_place = place_builder_resolved.into_place(this);
|
||||
this.cfg.push_fake_read(
|
||||
block,
|
||||
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
|
||||
// is same as that of the capture in the parent closure.
|
||||
PlaceBase::Upvar { .. } => {
|
||||
let enclosing_upvars_resolved =
|
||||
arg_place_builder.clone().into_place(this.tcx, this.typeck_results);
|
||||
let enclosing_upvars_resolved = arg_place_builder.clone().into_place(this);
|
||||
|
||||
match enclosing_upvars_resolved.as_ref() {
|
||||
PlaceRef {
|
||||
@ -654,7 +650,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
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(
|
||||
block,
|
||||
|
@ -23,6 +23,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr, mutability))
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn as_temp_inner(
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
@ -30,10 +31,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
expr: &Expr<'tcx>,
|
||||
mutability: Mutability,
|
||||
) -> BlockAnd<Local> {
|
||||
debug!(
|
||||
"as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
|
||||
block, temp_lifetime, expr, mutability
|
||||
);
|
||||
let this = self;
|
||||
|
||||
let expr_span = expr.span;
|
||||
|
@ -15,14 +15,13 @@ use std::iter;
|
||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
/// Compile `expr`, storing the result into `destination`, which
|
||||
/// is assumed to be uninitialized.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn expr_into_dest(
|
||||
&mut self,
|
||||
destination: Place<'tcx>,
|
||||
mut block: BasicBlock,
|
||||
expr: &Expr<'tcx>,
|
||||
) -> BlockAnd<()> {
|
||||
debug!("expr_into_dest(destination={:?}, block={:?}, expr={:?})", destination, block, expr);
|
||||
|
||||
// since we frequently have to reference `self` from within a
|
||||
// closure, where `self` would be shadowed, it's easier to
|
||||
// just use the name `this` uniformly
|
||||
@ -366,9 +365,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
None => {
|
||||
let place_builder = place_builder.clone();
|
||||
this.consume_by_copy_or_move(
|
||||
place_builder
|
||||
.field(n, *ty)
|
||||
.into_place(this.tcx, this.typeck_results),
|
||||
place_builder.field(n, *ty).into_place(this),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
@ -220,10 +220,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
|
||||
let source_info = self.source_info(scrutinee_span);
|
||||
|
||||
if let Ok(scrutinee_builder) =
|
||||
scrutinee_place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
||||
{
|
||||
let scrutinee_place = scrutinee_builder.into_place(self.tcx, self.typeck_results);
|
||||
if let Ok(scrutinee_builder) = scrutinee_place_builder.clone().try_upvars_resolved(self) {
|
||||
let scrutinee_place = scrutinee_builder.into_place(self);
|
||||
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 scrutinee_place: Place<'tcx>;
|
||||
if let Ok(scrutinee_builder) = scrutinee_place_builder
|
||||
.clone()
|
||||
.try_upvars_resolved(this.tcx, this.typeck_results)
|
||||
if let Ok(scrutinee_builder) =
|
||||
scrutinee_place_builder.clone().try_upvars_resolved(this)
|
||||
{
|
||||
scrutinee_place =
|
||||
scrutinee_builder.into_place(this.tcx, this.typeck_results);
|
||||
scrutinee_place = scrutinee_builder.into_place(this);
|
||||
opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span));
|
||||
}
|
||||
let scope = this.declare_bindings(
|
||||
@ -602,12 +598,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
while let Some(next) = {
|
||||
for binding in &candidate_ref.bindings {
|
||||
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
|
||||
// `PlaceBuilder` inside a closure. In this case, we don't want to include
|
||||
// a scrutinee place. `scrutinee_place_builder` will fail for destructured
|
||||
@ -622,10 +612,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
// let (v1, v2) = foo;
|
||||
// };
|
||||
// ```
|
||||
if let Ok(match_pair_resolved) =
|
||||
initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
||||
{
|
||||
let place = match_pair_resolved.into_place(self.tcx, self.typeck_results);
|
||||
if let Ok(match_pair_resolved) = initializer.clone().try_upvars_resolved(self) {
|
||||
let place = match_pair_resolved.into_place(self);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -654,6 +649,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
/// 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
|
||||
/// drop scope.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub(crate) fn declare_bindings(
|
||||
&mut self,
|
||||
mut visibility_scope: Option<SourceScope>,
|
||||
@ -662,7 +658,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
has_guard: ArmHasGuard,
|
||||
opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,
|
||||
) -> Option<SourceScope> {
|
||||
debug!("declare_bindings: pattern={:?}", pattern);
|
||||
self.visit_primary_bindings(
|
||||
&pattern,
|
||||
UserTypeProjections::none(),
|
||||
@ -872,7 +867,7 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
|
||||
Candidate {
|
||||
span: pattern.span,
|
||||
has_guard,
|
||||
match_pairs: smallvec![MatchPair { place, pattern }],
|
||||
match_pairs: smallvec![MatchPair::new(place, pattern)],
|
||||
bindings: Vec::new(),
|
||||
ascriptions: 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
|
||||
/// runtime) case when `x.0` is now `true`, we branch to
|
||||
/// `otherwise_block`.
|
||||
#[instrument(skip(self, fake_borrows), level = "debug")]
|
||||
fn match_candidates<'pat>(
|
||||
&mut self,
|
||||
span: Span,
|
||||
@ -1057,11 +1053,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
candidates: &mut [&mut Candidate<'pat, '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
|
||||
// the match pairs which remain require some form of test, whether it
|
||||
// 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>(
|
||||
&mut self,
|
||||
candidate: &mut Candidate<'pat, 'tcx>,
|
||||
@ -1389,7 +1384,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
place: PlaceBuilder<'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
|
||||
.iter()
|
||||
.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.
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1634,9 +1629,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
candidates = rest;
|
||||
}
|
||||
// at least the first candidate ought to be tested
|
||||
assert!(total_candidate_count > candidates.len());
|
||||
debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len());
|
||||
debug!("test_candidates: untested_candidates: {}", candidates.len());
|
||||
assert!(
|
||||
total_candidate_count > 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
|
||||
// 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 expr_place: Place<'tcx>;
|
||||
if let Ok(expr_builder) =
|
||||
expr_place_builder.try_upvars_resolved(self.tcx, self.typeck_results)
|
||||
{
|
||||
expr_place = expr_builder.into_place(self.tcx, self.typeck_results);
|
||||
if let Ok(expr_builder) = expr_place_builder.try_upvars_resolved(self) {
|
||||
expr_place = expr_builder.into_place(self);
|
||||
opt_expr_place = Some((Some(&expr_place), expr_span));
|
||||
}
|
||||
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
|
||||
/// will have type `&T`. The second local is a binding for occurrences of
|
||||
/// `var` in the arm body, which will have type `T`.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn declare_binding(
|
||||
&mut self,
|
||||
source_info: SourceInfo,
|
||||
@ -2209,19 +2208,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
opt_match_place: Option<(Option<Place<'tcx>>, 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 debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope };
|
||||
let binding_mode = match mode {
|
||||
BindingMode::ByValue => ty::BindingMode::BindByValue(mutability),
|
||||
BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability),
|
||||
};
|
||||
debug!("declare_binding: user_ty={:?}", user_ty);
|
||||
let local = LocalDecl::<'tcx> {
|
||||
mutability,
|
||||
ty: var_ty,
|
||||
@ -2271,7 +2263,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
} else {
|
||||
LocalsForNode::One(for_arm_body)
|
||||
};
|
||||
debug!("declare_binding: vars={:?}", locals);
|
||||
debug!(?locals);
|
||||
self.var_indices.insert(var_id, locals);
|
||||
}
|
||||
|
||||
|
@ -37,12 +37,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
///
|
||||
/// only generates a single switch. If this happens this method returns
|
||||
/// `true`.
|
||||
#[instrument(skip(self, candidate), level = "debug")]
|
||||
pub(super) fn simplify_candidate<'pat>(
|
||||
&mut self,
|
||||
candidate: &mut Candidate<'pat, 'tcx>,
|
||||
) -> bool {
|
||||
// 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.
|
||||
// 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 },
|
||||
} => {
|
||||
// Apply the type ascription to the value at `match_pair.place`, which is the
|
||||
if let Ok(place_resolved) =
|
||||
match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
||||
{
|
||||
if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self) {
|
||||
candidate.ascriptions.push(Ascription {
|
||||
annotation: annotation.clone(),
|
||||
source: place_resolved.into_place(self.tcx, self.typeck_results),
|
||||
source: place_resolved.into_place(self),
|
||||
variance,
|
||||
});
|
||||
}
|
||||
@ -184,12 +183,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
ref subpattern,
|
||||
is_primary: _,
|
||||
} => {
|
||||
if let Ok(place_resolved) =
|
||||
match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
||||
{
|
||||
if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self) {
|
||||
candidate.bindings.push(Binding {
|
||||
span: match_pair.pattern.span,
|
||||
source: place_resolved.into_place(self.tcx, self.typeck_results),
|
||||
source: place_resolved.into_place(self),
|
||||
var_id: var,
|
||||
binding_mode: mode,
|
||||
});
|
||||
|
@ -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(
|
||||
&mut self,
|
||||
match_start_span: Span,
|
||||
@ -153,21 +154,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
test: &Test<'tcx>,
|
||||
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
|
||||
) {
|
||||
let place: Place<'tcx>;
|
||||
if let Ok(test_place_builder) =
|
||||
place_builder.try_upvars_resolved(self.tcx, self.typeck_results)
|
||||
{
|
||||
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 place = place_builder.into_place(self);
|
||||
let place_ty = place.ty(&self.local_decls, self.tcx);
|
||||
debug!(?place, ?place_ty,);
|
||||
|
||||
let source_info = self.source_info(test.span);
|
||||
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)`,
|
||||
// we want to create a set of derived match-patterns like
|
||||
// `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
|
||||
let elem =
|
||||
ProjectionElem::Downcast(Some(adt_def.variant(variant_index).name), variant_index);
|
||||
let downcast_place = match_pair.place.project(elem); // `(x as Variant)`
|
||||
let downcast_place = match_pair.place.downcast(adt_def, variant_index); // `(x as Variant)`
|
||||
let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
|
||||
// e.g., `(x as Variant).0`
|
||||
let place = downcast_place.clone().field(subpattern.field, subpattern.pattern.ty);
|
||||
|
@ -31,21 +31,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
suffix: &'pat [Pat<'tcx>],
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
let (min_length, exact_size) = if let Ok(place_resolved) =
|
||||
place.clone().try_upvars_resolved(tcx, self.typeck_results)
|
||||
{
|
||||
match place_resolved
|
||||
.into_place(tcx, self.typeck_results)
|
||||
.ty(&self.local_decls, tcx)
|
||||
.ty
|
||||
.kind()
|
||||
{
|
||||
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)
|
||||
};
|
||||
let (min_length, exact_size) =
|
||||
if let Ok(place_resolved) = place.clone().try_upvars_resolved(self) {
|
||||
match place_resolved.into_place(self).ty(&self.local_decls, tcx).ty.kind() {
|
||||
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)| {
|
||||
let elem =
|
||||
@ -100,10 +94,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
|
||||
pub(crate) fn new(
|
||||
pub(in crate::build) fn new(
|
||||
place: PlaceBuilder<'tcx>,
|
||||
pattern: &'pat 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 }
|
||||
}
|
||||
}
|
||||
|
@ -553,6 +553,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|
||||
/// Convenience wrapper that pushes a scope and then executes `f`
|
||||
/// to build its contents, popping the scope afterwards.
|
||||
#[instrument(skip(self, f), level = "debug")]
|
||||
pub(crate) fn in_scope<F, R>(
|
||||
&mut self,
|
||||
region_scope: (region::Scope, SourceInfo),
|
||||
@ -562,7 +563,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
where
|
||||
F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
|
||||
{
|
||||
debug!("in_scope(region_scope={:?})", region_scope);
|
||||
let source_scope = self.source_scope;
|
||||
let tcx = self.tcx;
|
||||
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));
|
||||
unpack!(block = self.pop_scope(region_scope, block));
|
||||
self.source_scope = source_scope;
|
||||
debug!("in_scope: exiting region_scope={:?} block={:?}", region_scope, block);
|
||||
debug!(?block);
|
||||
block.and(rv)
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,8 @@ impl<'tcx> Cx<'tcx> {
|
||||
_ => None,
|
||||
};
|
||||
|
||||
trace!(?expr.ty);
|
||||
|
||||
// Now apply adjustments, if any.
|
||||
for adjustment in self.typeck_results.expr_adjustments(hir_expr) {
|
||||
trace!(?expr, ?adjustment);
|
||||
@ -56,6 +58,8 @@ impl<'tcx> Cx<'tcx> {
|
||||
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.
|
||||
expr = Expr {
|
||||
temp_lifetime,
|
||||
|
@ -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
|
||||
/// length of `constructor.arity()`.
|
||||
pub(super) fn wildcards(
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
constructor: &Constructor<'tcx>,
|
||||
) -> Self {
|
||||
#[instrument(level = "trace")]
|
||||
pub(super) fn wildcards(pcx: PatCtxt<'_, 'p, 'tcx>, constructor: &Constructor<'tcx>) -> Self {
|
||||
let ret = match constructor {
|
||||
Single | Variant(_) => match ty.kind() {
|
||||
ty::Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter()),
|
||||
ty::Ref(_, rty, _) => Fields::wildcards_from_tys(cx, once(*rty)),
|
||||
Single | Variant(_) => match pcx.ty.kind() {
|
||||
ty::Tuple(fs) => Fields::wildcards_from_tys(pcx.cx, fs.iter()),
|
||||
ty::Ref(_, rty, _) => Fields::wildcards_from_tys(pcx.cx, once(*rty)),
|
||||
ty::Adt(adt, substs) => {
|
||||
if adt.is_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.
|
||||
Fields::wildcards_from_tys(cx, once(substs.type_at(0)))
|
||||
Fields::wildcards_from_tys(pcx.cx, once(substs.type_at(0)))
|
||||
} else {
|
||||
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);
|
||||
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, _) => {
|
||||
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(..)
|
||||
| FloatRange(..)
|
||||
@ -1243,7 +1240,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
|
||||
bug!("called `Fields::wildcards` on an `Or` ctor")
|
||||
}
|
||||
};
|
||||
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
|
||||
debug!(?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
|
||||
/// `Some(_)`.
|
||||
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)
|
||||
}
|
||||
|
||||
@ -1553,13 +1550,13 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
|
||||
/// `other_ctor` can be different from `self.ctor`, but must be covered by it.
|
||||
pub(super) fn specialize<'a>(
|
||||
&'a self,
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
pcx: PatCtxt<'_, 'p, 'tcx>,
|
||||
other_ctor: &Constructor<'tcx>,
|
||||
) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> {
|
||||
match (&self.ctor, other_ctor) {
|
||||
(Wildcard, _) => {
|
||||
// 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))
|
||||
if self_slice.arity() != other_slice.arity() =>
|
||||
@ -1578,7 +1575,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
|
||||
let prefix = &self.fields.fields[..prefix];
|
||||
let suffix = &self.fields.fields[self_slice.arity() - suffix..];
|
||||
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 = (0..extra_wildcards).map(|_| wildcard);
|
||||
prefix.iter().chain(extra_wildcards).chain(suffix).collect()
|
||||
|
@ -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> {
|
||||
let mut ty = self.typeck_results.node_type(pat.hir_id);
|
||||
|
||||
|
@ -411,12 +411,12 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
|
||||
/// This is roughly the inverse of `Constructor::apply`.
|
||||
fn pop_head_constructor(
|
||||
&self,
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
pcx: PatCtxt<'_, 'p, 'tcx>,
|
||||
ctor: &Constructor<'tcx>,
|
||||
) -> PatStack<'p, 'tcx> {
|
||||
// We pop the head pattern and push the new fields extracted from the arguments of
|
||||
// `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..]);
|
||||
PatStack::from_vec(new_fields)
|
||||
}
|
||||
@ -475,7 +475,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
|
||||
let mut matrix = Matrix::empty();
|
||||
for row in &self.patterns {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -786,7 +786,7 @@ fn is_useful<'p, 'tcx>(
|
||||
is_under_guard: bool,
|
||||
is_top_level: bool,
|
||||
) -> Usefulness<'p, 'tcx> {
|
||||
debug!("matrix,v={:?}{:?}", matrix, v);
|
||||
debug!(?matrix, ?v);
|
||||
let Matrix { patterns: rows, .. } = matrix;
|
||||
|
||||
// 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()));
|
||||
|
||||
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.
|
||||
let mut ret = Usefulness::new_not_useful(witness_preference);
|
||||
if v.head().is_or_pat() {
|
||||
@ -832,6 +827,19 @@ fn is_useful<'p, 'tcx>(
|
||||
}
|
||||
}
|
||||
} 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();
|
||||
debug!(?v_ctor);
|
||||
if let Constructor::IntRange(ctor_range) = &v_ctor {
|
||||
@ -853,7 +861,7 @@ fn is_useful<'p, 'tcx>(
|
||||
debug!("specialize({:?})", ctor);
|
||||
// We cache the result of `Fields::wildcards` because it is used a lot.
|
||||
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(|| {
|
||||
is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false)
|
||||
});
|
||||
|
@ -48,6 +48,7 @@ impl<'tcx> Lift for PlaceElem<'tcx> {
|
||||
match *self {
|
||||
ProjectionElem::Deref => ProjectionElem::Deref,
|
||||
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::Subslice { from, to, from_end } => {
|
||||
ProjectionElem::Subslice { from, to, from_end }
|
||||
|
@ -28,6 +28,7 @@ fn is_stable(place: PlaceRef<'_>) -> bool {
|
||||
ProjectionElem::Field { .. } |
|
||||
ProjectionElem::ConstantIndex { .. } |
|
||||
ProjectionElem::Subslice { .. } |
|
||||
ProjectionElem::OpaqueCast { .. } |
|
||||
ProjectionElem::Downcast { .. } => true,
|
||||
}
|
||||
})
|
||||
|
@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
|
@ -4,9 +4,10 @@
|
||||
//! conflicts between multiple such attributes attached to the same
|
||||
//! item.
|
||||
|
||||
use crate::errors;
|
||||
use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
|
||||
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_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
|
||||
use rustc_hir as hir;
|
||||
@ -175,16 +176,20 @@ impl CheckAttrVisitor<'_> {
|
||||
if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
|
||||
attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
|
||||
{
|
||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
||||
let msg = match attr.style {
|
||||
ast::AttrStyle::Outer => {
|
||||
"crate-level attribute should be an inner attribute: add an exclamation \
|
||||
mark: `#![foo]`"
|
||||
}
|
||||
ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
|
||||
};
|
||||
lint.build(msg).emit();
|
||||
});
|
||||
match attr.style {
|
||||
ast::AttrStyle::Outer => self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::OuterCrateLevelAttr,
|
||||
),
|
||||
ast::AttrStyle::Inner => self.tcx.emit_spanned_lint(
|
||||
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) {
|
||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
||||
lint.build(&format!(
|
||||
"`#[{sym}]` is ignored on struct fields, match arms and macro defs",
|
||||
))
|
||||
.warn(
|
||||
"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();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::IgnoredAttrWithMacro { sym },
|
||||
);
|
||||
}
|
||||
|
||||
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| {
|
||||
lint.build(&format!("`#[{sym}]` is ignored on struct fields and match arms"))
|
||||
.warn(
|
||||
"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();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::IgnoredAttr { sym },
|
||||
);
|
||||
}
|
||||
|
||||
/// 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::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
|
||||
Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
|
||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
||||
lint.build("`#[inline]` is ignored on function prototypes").emit();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::IgnoredInlineAttrFnProto,
|
||||
);
|
||||
true
|
||||
}
|
||||
// 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
|
||||
// error here.
|
||||
Target::AssocConst => {
|
||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
||||
lint.build("`#[inline]` is ignored on constants")
|
||||
.warn(
|
||||
"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 #65833 <https://github.com/rust-lang/rust/issues/65833> \
|
||||
for more information",
|
||||
)
|
||||
.emit();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::IgnoredInlineAttrConstants,
|
||||
);
|
||||
true
|
||||
}
|
||||
// FIXME(#80564): Same for fields, arms, and macro defs
|
||||
@ -280,14 +265,10 @@ impl CheckAttrVisitor<'_> {
|
||||
true
|
||||
}
|
||||
_ => {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
attr.span,
|
||||
E0518,
|
||||
"attribute should be applied to function or closure",
|
||||
)
|
||||
.span_label(span, "not a function or closure")
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::InlineNotFnOrClosure {
|
||||
attr_span: attr.span,
|
||||
defn_span: span,
|
||||
});
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -309,36 +290,40 @@ impl CheckAttrVisitor<'_> {
|
||||
|
||||
// function prototypes can't be covered
|
||||
Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
|
||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
||||
lint.build("`#[no_coverage]` is ignored on function prototypes").emit();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::IgnoredNoCoverageFnProto,
|
||||
);
|
||||
true
|
||||
}
|
||||
|
||||
Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => {
|
||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
||||
lint.build("`#[no_coverage]` does not propagate into items and must be applied to the contained functions directly").emit();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::IgnoredNoCoveragePropagate,
|
||||
);
|
||||
true
|
||||
}
|
||||
|
||||
Target::Expression | Target::Statement | Target::Arm => {
|
||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
||||
lint.build("`#[no_coverage]` may only be applied to function definitions")
|
||||
.emit();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::IgnoredNoCoverageFnDefn,
|
||||
);
|
||||
true
|
||||
}
|
||||
|
||||
_ => {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
attr.span,
|
||||
E0788,
|
||||
"`#[no_coverage]` must be applied to coverable code",
|
||||
)
|
||||
.span_label(span, "not coverable code")
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::IgnoredNoCoverageNotCoverable {
|
||||
attr_span: attr.span,
|
||||
defn_span: span,
|
||||
});
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -389,14 +374,10 @@ impl CheckAttrVisitor<'_> {
|
||||
true
|
||||
}
|
||||
_ => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
attr.span,
|
||||
"attribute should be applied to a function definition",
|
||||
)
|
||||
.span_label(span, "not a function definition")
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
|
||||
attr_span: attr.span,
|
||||
defn_span: span,
|
||||
});
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -408,14 +389,10 @@ impl CheckAttrVisitor<'_> {
|
||||
Target::Fn
|
||||
| Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
|
||||
_ => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
attr.span,
|
||||
"attribute should be applied to a function definition",
|
||||
)
|
||||
.span_label(span, "not a function definition")
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
|
||||
attr_span: attr.span,
|
||||
defn_span: span,
|
||||
});
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -432,13 +409,7 @@ impl CheckAttrVisitor<'_> {
|
||||
) -> bool {
|
||||
match target {
|
||||
_ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
attr_span,
|
||||
E0736,
|
||||
"cannot use `#[track_caller]` with `#[naked]`",
|
||||
)
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::NakedTrackedCaller { attr_span });
|
||||
false
|
||||
}
|
||||
Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
|
||||
@ -453,14 +424,9 @@ impl CheckAttrVisitor<'_> {
|
||||
true
|
||||
}
|
||||
_ => {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
attr_span,
|
||||
E0739,
|
||||
"attribute should be applied to function"
|
||||
)
|
||||
.span_label(span, "not a function")
|
||||
.emit();
|
||||
self.tcx
|
||||
.sess
|
||||
.emit_err(errors::TrackedCallerWrongLocation { attr_span, defn_span: span });
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -485,14 +451,10 @@ impl CheckAttrVisitor<'_> {
|
||||
true
|
||||
}
|
||||
_ => {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
attr.span,
|
||||
E0701,
|
||||
"attribute can only be applied to a struct or enum"
|
||||
)
|
||||
.span_label(span, "not a struct or enum")
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::NonExhaustiveWrongLocation {
|
||||
attr_span: attr.span,
|
||||
defn_span: span,
|
||||
});
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -511,11 +473,10 @@ impl CheckAttrVisitor<'_> {
|
||||
true
|
||||
}
|
||||
_ => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(attr.span, "attribute can only be applied to a trait")
|
||||
.span_label(span, "not a trait")
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
|
||||
attr_span: attr.span,
|
||||
defn_span: span,
|
||||
});
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -531,11 +492,10 @@ impl CheckAttrVisitor<'_> {
|
||||
match target {
|
||||
Target::Trait => true,
|
||||
_ => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(attr.span, "attribute can only be applied to a trait")
|
||||
.span_label(span, "not a trait")
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
|
||||
attr_span: attr.span,
|
||||
defn_span: span,
|
||||
});
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -555,16 +515,12 @@ impl CheckAttrVisitor<'_> {
|
||||
// FIXME: #[target_feature] was previously erroneously allowed on statements and some
|
||||
// crates used this, so only emit a warning.
|
||||
Target::Statement => {
|
||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
||||
lint.build("attribute should be applied to a function")
|
||||
.warn(
|
||||
"this was previously accepted by the compiler but is \
|
||||
being phased out; it will become a hard error in \
|
||||
a future release!",
|
||||
)
|
||||
.span_label(span, "not a function")
|
||||
.emit();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::TargetFeatureOnStatement,
|
||||
);
|
||||
true
|
||||
}
|
||||
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
|
||||
@ -576,11 +532,10 @@ impl CheckAttrVisitor<'_> {
|
||||
true
|
||||
}
|
||||
_ => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(attr.span, "attribute should be applied to a function")
|
||||
.span_label(span, "not a function")
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
|
||||
attr_span: attr.span,
|
||||
defn_span: span,
|
||||
});
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -591,24 +546,17 @@ impl CheckAttrVisitor<'_> {
|
||||
match target {
|
||||
Target::ForeignStatic | Target::Static => true,
|
||||
_ => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(attr.span, "attribute should be applied to a static")
|
||||
.span_label(span, "not a static")
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToStatic {
|
||||
attr_span: attr.span,
|
||||
defn_span: span,
|
||||
});
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
meta.span(),
|
||||
&format!("doc {0} attribute expects a string: #[doc({0} = \"a\")]", attr_name),
|
||||
)
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name });
|
||||
}
|
||||
|
||||
fn check_doc_alias_value(
|
||||
@ -621,22 +569,12 @@ impl CheckAttrVisitor<'_> {
|
||||
aliases: &mut FxHashMap<String, Span>,
|
||||
) -> bool {
|
||||
let tcx = self.tcx;
|
||||
let err_fn = move |span: Span, msg: &str| {
|
||||
tcx.sess.span_err(
|
||||
span,
|
||||
&format!(
|
||||
"`#[doc(alias{})]` {}",
|
||||
if is_list { "(\"...\")" } else { " = \"...\"" },
|
||||
msg,
|
||||
),
|
||||
);
|
||||
false
|
||||
};
|
||||
let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span());
|
||||
let attr_str =
|
||||
&format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" });
|
||||
if doc_alias == kw::Empty {
|
||||
return err_fn(
|
||||
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
||||
"attribute cannot have empty value",
|
||||
);
|
||||
tcx.sess.emit_err(errors::DocAliasEmpty { span, attr_str });
|
||||
return false;
|
||||
}
|
||||
|
||||
let doc_alias_str = doc_alias.as_str();
|
||||
@ -644,23 +582,16 @@ impl CheckAttrVisitor<'_> {
|
||||
.chars()
|
||||
.find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
|
||||
{
|
||||
self.tcx.sess.span_err(
|
||||
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
||||
&format!(
|
||||
"{:?} character isn't allowed in `#[doc(alias{})]`",
|
||||
c,
|
||||
if is_list { "(\"...\")" } else { " = \"...\"" },
|
||||
),
|
||||
);
|
||||
tcx.sess.emit_err(errors::DocAliasBadChar { span, attr_str, char_: c });
|
||||
return false;
|
||||
}
|
||||
if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') {
|
||||
return err_fn(
|
||||
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
||||
"cannot start or end with ' '",
|
||||
);
|
||||
tcx.sess.emit_err(errors::DocAliasStartEnd { span, attr_str });
|
||||
return false;
|
||||
}
|
||||
if let Some(err) = match target {
|
||||
|
||||
let span = meta.span();
|
||||
if let Some(location) = match target {
|
||||
Target::Impl => Some("implementation block"),
|
||||
Target::ForeignMod => Some("extern block"),
|
||||
Target::AssocTy => {
|
||||
@ -686,19 +617,21 @@ impl CheckAttrVisitor<'_> {
|
||||
Target::Param => return false,
|
||||
_ => 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);
|
||||
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) {
|
||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, span, |lint| {
|
||||
lint.build("doc alias is duplicated")
|
||||
.span_label(*entry.entry.get(), "first defined here")
|
||||
.emit();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
span,
|
||||
errors::DocAliasDuplicated { first_defn: *entry.entry.get() },
|
||||
);
|
||||
}
|
||||
true
|
||||
}
|
||||
@ -723,22 +656,12 @@ impl CheckAttrVisitor<'_> {
|
||||
_ => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
v.span(),
|
||||
"`#[doc(alias(\"a\"))]` expects string literals",
|
||||
)
|
||||
.emit();
|
||||
.emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
|
||||
errors += 1;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
v.span(),
|
||||
"`#[doc(alias(\"a\"))]` expects string literals",
|
||||
)
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
|
||||
errors += 1;
|
||||
}
|
||||
}
|
||||
@ -747,14 +670,7 @@ impl CheckAttrVisitor<'_> {
|
||||
} else if let Some(doc_alias) = meta.value_str() {
|
||||
self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases)
|
||||
} else {
|
||||
self.tcx
|
||||
.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();
|
||||
self.tcx.sess.emit_err(errors::DocAliasMalformed { span: meta.span() });
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -771,35 +687,20 @@ impl CheckAttrVisitor<'_> {
|
||||
}) {
|
||||
Some(ItemKind::Mod(ref module)) => {
|
||||
if !module.item_ids.is_empty() {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
meta.span(),
|
||||
"`#[doc(keyword = \"...\")]` can only be used on empty modules",
|
||||
)
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::DocKeywordEmptyMod { span: meta.span() });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
meta.span(),
|
||||
"`#[doc(keyword = \"...\")]` can only be used on modules",
|
||||
)
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::DocKeywordNotMod { span: meta.span() });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if !rustc_lexer::is_ident(doc_keyword.as_str()) {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
||||
&format!("`{doc_keyword}` is not a valid identifier"),
|
||||
)
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::DocKeywordInvalidIdent {
|
||||
span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
||||
doc_keyword,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
true
|
||||
@ -812,24 +713,12 @@ impl CheckAttrVisitor<'_> {
|
||||
}) {
|
||||
Some(ItemKind::Impl(ref i)) => {
|
||||
if !matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) {
|
||||
self.tcx
|
||||
.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();
|
||||
self.tcx.sess.emit_err(errors::DocTupleVariadicNotFirst { span: meta.span() });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
meta.span(),
|
||||
"`#[doc(keyword = \"...\")]` can only be used on impl blocks",
|
||||
)
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::DocKeywordOnlyImpl { span: meta.span() });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -858,13 +747,9 @@ impl CheckAttrVisitor<'_> {
|
||||
if let Some((prev_inline, prev_span)) = *specified_inline {
|
||||
if do_inline != prev_inline {
|
||||
let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
|
||||
spans.push_span_label(prev_span, "this attribute...");
|
||||
spans.push_span_label(meta.span(), "...conflicts with this attribute");
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(spans, "conflicting doc inlining attributes")
|
||||
.help("remove one of the conflicting attributes")
|
||||
.emit();
|
||||
spans.push_span_label(prev_span, fluent::passes::doc_inline_conflict_first);
|
||||
spans.push_span_label(meta.span(), fluent::passes::doc_inline_conflict_second);
|
||||
self.tcx.sess.emit_err(errors::DocKeywordConflict { spans });
|
||||
return false;
|
||||
}
|
||||
true
|
||||
@ -873,23 +758,14 @@ impl CheckAttrVisitor<'_> {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
self.tcx.struct_span_lint_hir(
|
||||
self.tcx.emit_spanned_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span(),
|
||||
|lint| {
|
||||
let mut err = lint.build(
|
||||
"this attribute can only be applied to a `use` item",
|
||||
);
|
||||
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();
|
||||
errors::DocInlineOnlyUse {
|
||||
attr_span: meta.span(),
|
||||
item_span: (attr.style == AttrStyle::Outer)
|
||||
.then(|| self.tcx.hir().span(hir_id)),
|
||||
},
|
||||
);
|
||||
false
|
||||
@ -904,15 +780,7 @@ impl CheckAttrVisitor<'_> {
|
||||
attr_name: &str,
|
||||
) -> bool {
|
||||
if CRATE_HIR_ID == hir_id {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
meta.span(),
|
||||
&format!(
|
||||
"`#![doc({attr_name} = \"...\")]` isn't allowed as a crate-level attribute",
|
||||
),
|
||||
)
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name });
|
||||
return false;
|
||||
}
|
||||
true
|
||||
@ -926,36 +794,25 @@ impl CheckAttrVisitor<'_> {
|
||||
hir_id: HirId,
|
||||
) -> bool {
|
||||
if hir_id != CRATE_HIR_ID {
|
||||
self.tcx.struct_span_lint_hir(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span(),
|
||||
|lint| {
|
||||
let mut err = lint.build(
|
||||
"this attribute can only be applied at the crate level",
|
||||
);
|
||||
if attr.style == AttrStyle::Outer && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID {
|
||||
if let Ok(mut src) =
|
||||
self.tcx.sess.source_map().span_to_snippet(attr.span)
|
||||
{
|
||||
src.insert(1, '!');
|
||||
err.span_suggestion_verbose(
|
||||
attr.span,
|
||||
"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",
|
||||
);
|
||||
}
|
||||
self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| {
|
||||
let mut err = lint.build(fluent::passes::attr_crate_level);
|
||||
if attr.style == AttrStyle::Outer
|
||||
&& self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID
|
||||
{
|
||||
if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) {
|
||||
src.insert(1, '!');
|
||||
err.span_suggestion_verbose(
|
||||
attr.span,
|
||||
fluent::passes::suggestion,
|
||||
src,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
err.span_help(attr.span, fluent::passes::help);
|
||||
}
|
||||
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;
|
||||
}
|
||||
true
|
||||
@ -970,18 +827,14 @@ impl CheckAttrVisitor<'_> {
|
||||
match i_meta.name_or_empty() {
|
||||
sym::attr | sym::no_crate_inject => {}
|
||||
_ => {
|
||||
self.tcx.struct_span_lint_hir(
|
||||
self.tcx.emit_spanned_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
i_meta.span(),
|
||||
|lint| {
|
||||
lint.build(&format!(
|
||||
"unknown `doc(test)` attribute `{}`",
|
||||
rustc_ast_pretty::pprust::path_to_string(
|
||||
&i_meta.meta_item().unwrap().path
|
||||
),
|
||||
))
|
||||
.emit();
|
||||
errors::DocTestUnknown {
|
||||
path: rustc_ast_pretty::pprust::path_to_string(
|
||||
&i_meta.meta_item().unwrap().path,
|
||||
),
|
||||
},
|
||||
);
|
||||
is_valid = false;
|
||||
@ -989,9 +842,12 @@ impl CheckAttrVisitor<'_> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| {
|
||||
lint.build("`#[doc(test(...)]` takes a list of attributes").emit();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span(),
|
||||
errors::DocTestTakesList,
|
||||
);
|
||||
is_valid = false;
|
||||
}
|
||||
is_valid
|
||||
@ -1093,79 +949,66 @@ impl CheckAttrVisitor<'_> {
|
||||
|
||||
sym::primitive => {
|
||||
if !self.tcx.features().rustdoc_internals {
|
||||
self.tcx.struct_span_lint_hir(
|
||||
self.tcx.emit_spanned_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
i_meta.span,
|
||||
|lint| {
|
||||
let mut diag = lint.build(
|
||||
"`doc(primitive)` should never have been stable",
|
||||
);
|
||||
diag.emit();
|
||||
},
|
||||
errors::DocPrimitive,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
self.tcx.struct_span_lint_hir(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
i_meta.span,
|
||||
|lint| {
|
||||
let mut diag = lint.build(&format!(
|
||||
"unknown `doc` attribute `{}`",
|
||||
rustc_ast_pretty::pprust::path_to_string(&i_meta.path),
|
||||
));
|
||||
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");
|
||||
let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path);
|
||||
if i_meta.has_name(sym::spotlight) {
|
||||
self.tcx.emit_spanned_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
i_meta.span,
|
||||
errors::DocTestUnknownSpotlight {
|
||||
path,
|
||||
span: i_meta.span
|
||||
}
|
||||
if i_meta.has_name(sym::include) {
|
||||
if let Some(value) = i_meta.value_str() {
|
||||
// if there are multiple attributes, the suggestion would suggest deleting all of them, which is incorrect
|
||||
let applicability = if list.len() == 1 {
|
||||
Applicability::MachineApplicable
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
let inner = if attr.style == AttrStyle::Inner {
|
||||
"!"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
diag.span_suggestion(
|
||||
attr.meta().unwrap().span,
|
||||
"use `doc = include_str!` instead",
|
||||
format!(
|
||||
"#{inner}[doc = include_str!(\"{value}\")]",
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
);
|
||||
} else if i_meta.has_name(sym::include) &&
|
||||
let Some(value) = i_meta.value_str() {
|
||||
let applicability = if list.len() == 1 {
|
||||
Applicability::MachineApplicable
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
// If there are multiple attributes, the suggestion would suggest
|
||||
// deleting all of them, which is incorrect.
|
||||
self.tcx.emit_spanned_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
i_meta.span,
|
||||
errors::DocTestUnknownInclude {
|
||||
path,
|
||||
value: value.to_string(),
|
||||
inner: (attr.style == AttrStyle::Inner)
|
||||
.then_some("!")
|
||||
.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;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.tcx.struct_span_lint_hir(
|
||||
self.tcx.emit_spanned_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span(),
|
||||
|lint| {
|
||||
lint.build("invalid `doc` attribute").emit();
|
||||
},
|
||||
errors::DocInvalid,
|
||||
);
|
||||
is_valid = false;
|
||||
}
|
||||
@ -1180,14 +1023,7 @@ impl CheckAttrVisitor<'_> {
|
||||
match target {
|
||||
Target::Struct | Target::Enum | Target::TyAlias => true,
|
||||
_ => {
|
||||
self.tcx
|
||||
.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();
|
||||
self.tcx.sess.emit_err(errors::PassByValue { attr_span: attr.span, span });
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -1197,14 +1033,7 @@ impl CheckAttrVisitor<'_> {
|
||||
match target {
|
||||
Target::Method(MethodKind::Inherent) => true,
|
||||
_ => {
|
||||
self.tcx
|
||||
.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();
|
||||
self.tcx.sess.emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span });
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -1223,12 +1052,7 @@ impl CheckAttrVisitor<'_> {
|
||||
_ => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
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();
|
||||
.emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span });
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -1238,19 +1062,12 @@ impl CheckAttrVisitor<'_> {
|
||||
fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
|
||||
let node = self.tcx.hir().get(hir_id);
|
||||
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| {
|
||||
lint.build(
|
||||
"`must_use` attribute on `async` functions \
|
||||
applies to the anonymous `Future` returned by the \
|
||||
function, not the value within",
|
||||
)
|
||||
.span_label(
|
||||
span,
|
||||
"this attribute does nothing, the `Future`s \
|
||||
returned by async functions are already `must_use`",
|
||||
)
|
||||
.emit();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::MustUseAsync { span }
|
||||
);
|
||||
}
|
||||
|
||||
if !matches!(
|
||||
@ -1278,12 +1095,12 @@ impl CheckAttrVisitor<'_> {
|
||||
_ => "a",
|
||||
};
|
||||
|
||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
||||
lint.build(&format!(
|
||||
"`#[must_use]` has no effect when applied to {article} {target}"
|
||||
))
|
||||
.emit();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::MustUseNoEffect { article, target },
|
||||
);
|
||||
}
|
||||
|
||||
// For now, its always valid
|
||||
@ -1295,11 +1112,7 @@ impl CheckAttrVisitor<'_> {
|
||||
match target {
|
||||
Target::Struct | Target::Enum | Target::Union | Target::Trait => true,
|
||||
_ => {
|
||||
self.tcx
|
||||
.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();
|
||||
self.tcx.sess.emit_err(errors::MustNotSuspend { attr_span: attr.span, span });
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -1319,16 +1132,12 @@ impl CheckAttrVisitor<'_> {
|
||||
_ => {
|
||||
// FIXME: #[cold] was previously allowed on non-functions and some crates used
|
||||
// this, so only emit a warning.
|
||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
||||
lint.build("attribute should be applied to a function")
|
||||
.warn(
|
||||
"this was previously accepted by the compiler but is \
|
||||
being phased out; it will become a hard error in \
|
||||
a future release!",
|
||||
)
|
||||
.span_label(span, "not a function")
|
||||
.emit();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::Cold { span },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1343,19 +1152,12 @@ impl CheckAttrVisitor<'_> {
|
||||
return;
|
||||
}
|
||||
|
||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
||||
let mut diag =
|
||||
lint.build("attribute should be applied to an `extern` block with non-Rust ABI");
|
||||
diag.warn(
|
||||
"this was previously accepted by the compiler but is \
|
||||
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();
|
||||
});
|
||||
self.tcx.emit_spanned_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::Link { span: (target != Target::ForeignMod).then_some(span) },
|
||||
);
|
||||
}
|
||||
|
||||
/// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
|
||||
|
362
compiler/rustc_passes/src/errors.rs
Normal file
362
compiler/rustc_passes/src/errors.rs
Normal 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>,
|
||||
}
|
@ -7,8 +7,8 @@
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(let_else)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(let_else)]
|
||||
#![feature(map_try_insert)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(try_blocks)]
|
||||
@ -27,6 +27,7 @@ pub mod dead;
|
||||
mod debugger_visualizer;
|
||||
mod diagnostic_items;
|
||||
pub mod entry;
|
||||
mod errors;
|
||||
pub mod hir_id_validator;
|
||||
pub mod hir_stats;
|
||||
mod lang_items;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
|
||||
use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
#[derive(SessionDiagnostic)]
|
||||
@ -73,3 +73,19 @@ pub struct InPublicInterface<'a> {
|
||||
#[label(privacy::visibility_label)]
|
||||
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,
|
||||
}
|
||||
|
@ -38,8 +38,8 @@ use std::ops::ControlFlow;
|
||||
use std::{cmp, fmt, mem};
|
||||
|
||||
use errors::{
|
||||
FieldIsPrivate, FieldIsPrivateLabel, InPublicInterface, InPublicInterfaceTraits, ItemIsPrivate,
|
||||
UnnamedItemIsPrivate,
|
||||
FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface,
|
||||
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 {
|
||||
if self.leaks_private_dep(def_id) {
|
||||
self.tcx.struct_span_lint_hir(
|
||||
self.tcx.emit_spanned_lint(
|
||||
lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES,
|
||||
self.tcx.hir().local_def_id_to_hir_id(self.item_def_id),
|
||||
self.tcx.def_span(self.item_def_id.to_def_id()),
|
||||
|lint| {
|
||||
lint.build(&format!(
|
||||
"{} `{}` from private dependency '{}' in public \
|
||||
interface",
|
||||
kind,
|
||||
descr,
|
||||
self.tcx.crate_name(def_id.krate)
|
||||
))
|
||||
.emit();
|
||||
FromPrivateDependencyInPublicInterface {
|
||||
kind,
|
||||
descr: descr.to_string(),
|
||||
krate: self.tcx.crate_name(def_id.krate),
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -1754,12 +1749,14 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
|
||||
}
|
||||
};
|
||||
let span = self.tcx.def_span(self.item_def_id.to_def_id());
|
||||
let descr = descr.to_string();
|
||||
if self.has_old_errors
|
||||
|| self.in_assoc_ty
|
||||
|| self.tcx.resolutions(()).has_pub_restricted
|
||||
{
|
||||
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" {
|
||||
self.tcx.sess.emit_err(InPublicInterfaceTraits {
|
||||
span,
|
||||
@ -1778,19 +1775,11 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let err_code = if kind == "trait" { "E0445" } else { "E0446" };
|
||||
self.tcx.struct_span_lint_hir(
|
||||
self.tcx.emit_spanned_lint(
|
||||
lint::builtin::PRIVATE_IN_PUBLIC,
|
||||
hir_id,
|
||||
span,
|
||||
|lint| {
|
||||
lint.build(&format!(
|
||||
"{} (error {})",
|
||||
format!("{} {} `{}` in public interface", vis_descr, kind, descr),
|
||||
err_code
|
||||
))
|
||||
.emit();
|
||||
},
|
||||
PrivateInPublicLint { vis_descr, kind, descr },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::sync::Lock;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::abi::{Align, Size};
|
||||
use std::cmp::{self, Ordering};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct VariantInfo {
|
||||
pub name: Option<String>,
|
||||
pub name: Option<Symbol>,
|
||||
pub kind: SizeKind,
|
||||
pub size: u64,
|
||||
pub align: u64,
|
||||
@ -20,7 +21,7 @@ pub enum SizeKind {
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct FieldInfo {
|
||||
pub name: String,
|
||||
pub name: Symbol,
|
||||
pub offset: u64,
|
||||
pub size: u64,
|
||||
pub align: u64,
|
||||
@ -119,7 +120,7 @@ impl CodeStats {
|
||||
let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
|
||||
let indent = if !struct_like {
|
||||
let name = match name.as_ref() {
|
||||
Some(name) => name.to_owned(),
|
||||
Some(name) => name.to_string(),
|
||||
None => i.to_string(),
|
||||
};
|
||||
println!(
|
||||
|
@ -23,7 +23,7 @@ use rustc_hir::GenericParam;
|
||||
use rustc_hir::Item;
|
||||
use rustc_hir::Node;
|
||||
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::ty::abstract_const::NotConstEvaluatable;
|
||||
use rustc_middle::ty::error::ExpectedFound;
|
||||
@ -1403,7 +1403,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
|
||||
fn annotate_source_of_ambiguity(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
impls: &[AmbiguousSelection],
|
||||
impls: &[DefId],
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
);
|
||||
|
||||
@ -2036,14 +2036,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
||||
);
|
||||
match selcx.select_from_obligation(&obligation) {
|
||||
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);
|
||||
}
|
||||
_ => {
|
||||
@ -2224,35 +2216,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
||||
fn annotate_source_of_ambiguity(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
impls: &[AmbiguousSelection],
|
||||
impls: &[DefId],
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
) {
|
||||
let mut spans = vec![];
|
||||
let mut crates = vec![];
|
||||
let mut post = vec![];
|
||||
let mut or_where_clause = false;
|
||||
for ambig in impls {
|
||||
match ambig {
|
||||
AmbiguousSelection::Impl(def_id) => match self.tcx.span_of_impl(*def_id) {
|
||||
Ok(span) => spans.push(span),
|
||||
Err(name) => {
|
||||
crates.push(name);
|
||||
if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) {
|
||||
post.push(header);
|
||||
}
|
||||
for def_id in impls {
|
||||
match self.tcx.span_of_impl(*def_id) {
|
||||
Ok(span) => spans.push(span),
|
||||
Err(name) => {
|
||||
crates.push(name);
|
||||
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!(
|
||||
"multiple `impl`s{} satisfying `{}` found",
|
||||
if or_where_clause { " or `where` clauses" } else { "" },
|
||||
predicate
|
||||
);
|
||||
let msg = format!("multiple `impl`s satisfying `{}` found", predicate);
|
||||
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect();
|
||||
crate_names.sort();
|
||||
crate_names.dedup();
|
||||
|
@ -6,11 +6,9 @@
|
||||
//!
|
||||
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
|
||||
use hir::LangItem;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::traits::util::elaborate_predicates_with_span;
|
||||
use rustc_infer::traits::{AmbiguousSelection, TraitEngine};
|
||||
use rustc_infer::traits::TraitEngine;
|
||||
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
|
||||
use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
@ -201,48 +199,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
// and report ambiguity.
|
||||
if i > 1 {
|
||||
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(
|
||||
candidates
|
||||
.into_iter()
|
||||
.filter_map(|c| match c.candidate {
|
||||
SelectionCandidate::ImplCandidate(def_id) => {
|
||||
Some(AmbiguousSelection::Impl(def_id))
|
||||
}
|
||||
SelectionCandidate::ParamCandidate(predicate) => {
|
||||
Some(AmbiguousSelection::ParamEnv(
|
||||
*param_env_spans.get(&predicate)?,
|
||||
))
|
||||
}
|
||||
SelectionCandidate::ImplCandidate(def_id) => Some(def_id),
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
|
@ -2486,7 +2486,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
concrete type's name `{type_name}` instead if you want to \
|
||||
specify its type parameters"
|
||||
),
|
||||
type_name.to_string(),
|
||||
type_name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
@ -544,7 +544,7 @@ fn compare_self_type<'tcx>(
|
||||
if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
|
||||
err.span_label(span, format!("trait method declared without `{self_descr}`"));
|
||||
} 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();
|
||||
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) {
|
||||
err.span_label(span, format!("`{self_descr}` used in trait"));
|
||||
} 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();
|
||||
return Err(reported);
|
||||
@ -803,7 +803,7 @@ fn compare_number_of_method_arguments<'tcx>(
|
||||
),
|
||||
);
|
||||
} 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(
|
||||
impl_span,
|
||||
|
@ -1856,7 +1856,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let remaining_private_fields_len = remaining_private_fields.len();
|
||||
let names = match &remaining_private_fields
|
||||
.iter()
|
||||
.map(|(name, _, _)| name.to_string())
|
||||
.map(|(name, _, _)| name)
|
||||
.collect::<Vec<_>>()[..]
|
||||
{
|
||||
_ if remaining_private_fields_len > 6 => String::new(),
|
||||
|
@ -19,6 +19,7 @@ use rustc_middle::ty::print::with_crate_prefix;
|
||||
use rustc_middle::ty::ToPolyTraitRef;
|
||||
use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable};
|
||||
use rustc_span::symbol::{kw, sym, Ident};
|
||||
use rustc_span::Symbol;
|
||||
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::query::evaluate_obligation::InferCtxtExt as _;
|
||||
@ -1548,7 +1549,7 @@ impl<'a, 'tcx> FnCtxt<'a, '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();
|
||||
for (pred, _, _) in unsatisfied_predicates {
|
||||
let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue };
|
||||
@ -1581,12 +1582,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
derives.push((
|
||||
self_name.clone(),
|
||||
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 {
|
||||
traits.push(self.tcx.def_span(trait_pred.def_id()));
|
||||
}
|
||||
@ -1609,7 +1610,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
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();
|
||||
|
@ -25,7 +25,7 @@ can be broken down into several distinct phases:
|
||||
|
||||
- regionck: after main is complete, the regionck pass goes over all
|
||||
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
|
||||
flexibility.
|
||||
|
||||
|
@ -17,7 +17,7 @@ use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
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 { .. } => {
|
||||
let param_ty = ty::ParamTy::for_def(param);
|
||||
if !input_parameters.contains(&cgp::Parameter::from(param_ty)) {
|
||||
report_unused_parameter(
|
||||
tcx,
|
||||
tcx.def_span(param.def_id),
|
||||
"type",
|
||||
¶m_ty.to_string(),
|
||||
);
|
||||
report_unused_parameter(tcx, tcx.def_span(param.def_id), "type", param_ty.name);
|
||||
}
|
||||
}
|
||||
ty::GenericParamDefKind::Lifetime => {
|
||||
@ -140,7 +135,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
|
||||
tcx,
|
||||
tcx.def_span(param.def_id),
|
||||
"lifetime",
|
||||
¶m.name.to_string(),
|
||||
param.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -151,7 +146,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
|
||||
tcx,
|
||||
tcx.def_span(param.def_id),
|
||||
"const",
|
||||
¶m_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.
|
||||
}
|
||||
|
||||
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!(
|
||||
tcx.sess,
|
||||
span,
|
||||
|
@ -836,14 +836,14 @@ impl<T: Clone, V: Borrow<[T]>> Join<&[T]> for [V] {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[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] {
|
||||
&self[..]
|
||||
}
|
||||
}
|
||||
|
||||
#[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] {
|
||||
&mut self[..]
|
||||
}
|
||||
|
@ -2120,8 +2120,9 @@ fn clean_use_statement<'tcx>(
|
||||
// forcefully don't inline if this is not public or if the
|
||||
// #[doc(no_inline)] attribute is present.
|
||||
// Don't inline doc(hidden) imports so they can be stripped at a later stage.
|
||||
let mut denied = !(visibility.is_public()
|
||||
|| (cx.render_options.document_private && is_visible_from_parent_mod))
|
||||
let mut denied = cx.output_format.is_json()
|
||||
|| !(visibility.is_public()
|
||||
|| (cx.render_options.document_private && is_visible_from_parent_mod))
|
||||
|| pub_underscore
|
||||
|| attrs.iter().any(|a| {
|
||||
a.has_name(sym::doc)
|
||||
|
@ -81,6 +81,8 @@ pub(crate) struct DocContext<'tcx> {
|
||||
pub(crate) inlined: FxHashSet<ItemId>,
|
||||
/// Used by `calculate_doc_coverage`.
|
||||
pub(crate) output_format: OutputFormat,
|
||||
/// Used by `strip_private`.
|
||||
pub(crate) show_coverage: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> DocContext<'tcx> {
|
||||
@ -381,6 +383,7 @@ pub(crate) fn run_global_ctxt(
|
||||
inlined: FxHashSet::default(),
|
||||
output_format,
|
||||
render_options,
|
||||
show_coverage,
|
||||
};
|
||||
|
||||
// Small hack to force the Sized trait to be present.
|
||||
|
@ -43,7 +43,16 @@ impl JsonRenderer<'_> {
|
||||
let span = item.span(self.tcx);
|
||||
let clean::Item { name, attrs: _, kind: _, visibility, item_id, cfg: _ } = item;
|
||||
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),
|
||||
};
|
||||
Some(Item {
|
||||
@ -220,7 +229,9 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
|
||||
let header = item.fn_header(tcx);
|
||||
|
||||
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)),
|
||||
StructItem(s) => ItemEnum::Struct(s.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(),
|
||||
default: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
|
||||
},
|
||||
// `convert_item` early returns `None` for striped items and keywords.
|
||||
StrippedItem(_) | KeywordItem(_) => unreachable!(),
|
||||
// `convert_item` early returns `None` for stripped items and keywords.
|
||||
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 {
|
||||
name: name.as_ref().unwrap().to_string(),
|
||||
rename: src.map(|x| x.to_string()),
|
||||
|
@ -21,6 +21,7 @@ use rustc_span::def_id::LOCAL_CRATE;
|
||||
use rustdoc_json_types as types;
|
||||
|
||||
use crate::clean::types::{ExternalCrate, ExternalLocation};
|
||||
use crate::clean::ItemKind;
|
||||
use crate::config::RenderOptions;
|
||||
use crate::docfs::PathError;
|
||||
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
|
||||
/// implementations filled out before they're inserted.
|
||||
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
|
||||
item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap());
|
||||
|
||||
|
@ -24,6 +24,7 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) ->
|
||||
retained: &mut retained,
|
||||
access_levels: &cx.cache.access_levels,
|
||||
update_retained: true,
|
||||
is_json_output: cx.output_format.is_json() && !cx.show_coverage,
|
||||
};
|
||||
krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::middle::privacy::AccessLevels;
|
||||
use std::mem;
|
||||
|
||||
use crate::clean::{self, Item, ItemIdSet};
|
||||
use crate::clean::{self, Item, ItemId, ItemIdSet};
|
||||
use crate::fold::{strip_item, DocFolder};
|
||||
use crate::formats::cache::Cache;
|
||||
|
||||
@ -11,6 +11,21 @@ pub(crate) struct Stripper<'a> {
|
||||
pub(crate) retained: &'a mut ItemIdSet,
|
||||
pub(crate) access_levels: &'a AccessLevels<DefId>,
|
||||
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> {
|
||||
@ -45,9 +60,8 @@ impl<'a> DocFolder for Stripper<'a> {
|
||||
| clean::TraitAliasItem(..)
|
||||
| clean::MacroItem(..)
|
||||
| clean::ForeignTypeItem => {
|
||||
if i.item_id.is_local()
|
||||
&& !self.access_levels.is_exported(i.item_id.expect_def_id())
|
||||
{
|
||||
let item_id = i.item_id;
|
||||
if item_id.is_local() && !self.is_item_reachable(item_id) {
|
||||
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
|
||||
return None;
|
||||
}
|
||||
|
@ -190,6 +190,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||
) -> bool {
|
||||
debug!("maybe_inline_local res: {:?}", res);
|
||||
|
||||
if self.cx.output_format.is_json() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let tcx = self.cx.tcx;
|
||||
let Some(res_did) = res.opt_def_id() else {
|
||||
return false;
|
||||
|
@ -9,7 +9,7 @@ use std::path::PathBuf;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// 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
|
||||
/// 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 is_crate: bool,
|
||||
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)]
|
||||
|
@ -3,6 +3,8 @@
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
// First, we check that the search results are hidden when the Escape key is pressed.
|
||||
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
|
||||
// Check that the currently displayed element is search.
|
||||
wait-for: "#alternative-display #search"
|
||||
|
@ -2,6 +2,8 @@
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
show-text: true
|
||||
write: (".search-input", "test")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
assert-text: ("#results .externcrate", "test_docs")
|
||||
|
@ -7,6 +7,8 @@ reload:
|
||||
assert-text: ("//*[@id='reexport.TheStdReexport']", "pub use ::std as TheStdReexport;")
|
||||
assert-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"})
|
||||
write: (".search-input", "TheStdReexport")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
wait-for: "//a[@class='result-import']"
|
||||
assert-attribute: (
|
||||
"//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)"})
|
||||
|
||||
// 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")
|
||||
wait-for: "//a[@class='result-import']"
|
||||
assert-text: (
|
||||
|
@ -89,6 +89,8 @@ show-text: true
|
||||
// We reload the page so the local storage settings are being used.
|
||||
reload:
|
||||
write: (".search-input", "thisisanalias")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
// Checking that the colors for the alias element are the ones expected.
|
||||
|
@ -2,6 +2,8 @@
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
size: (900, 1000)
|
||||
write: (".search-input", "test")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
wait-for: "#search-settings"
|
||||
// The width is returned by "getComputedStyle" which returns the exact number instead of the
|
||||
// CSS rule which is "50%"...
|
||||
|
@ -1,6 +1,8 @@
|
||||
// Checks that the "keyword" results have the expected text alongside them.
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "CookieMonster")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
// Note: The two next assert commands could be merged as one but readability would be
|
||||
|
@ -2,6 +2,8 @@
|
||||
// First, try a search-by-name
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "Foo")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
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
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "-> String")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
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
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "-> Something")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
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
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "usize pattern")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
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
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "pattern -> str")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||
|
@ -20,6 +20,8 @@ wait-for-css: ("#settings", {"display": "none"})
|
||||
// Let's click on it when the search results are displayed.
|
||||
focus: ".search-input"
|
||||
write: "test"
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
wait-for: "#alternative-display #search"
|
||||
click: "#settings-menu"
|
||||
wait-for-css: ("#settings", {"display": "block"})
|
||||
|
22
src/test/rustdoc-json/doc_hidden_failure.rs
Normal file
22
src/test/rustdoc-json/doc_hidden_failure.rs
Normal 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::*;
|
||||
}
|
1
src/test/rustdoc-json/reexport/auxiliary/pub-struct.rs
Normal file
1
src/test/rustdoc-json/reexport/auxiliary/pub-struct.rs
Normal file
@ -0,0 +1 @@
|
||||
pub struct Foo;
|
@ -3,15 +3,16 @@
|
||||
#![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 {
|
||||
extern "C" {
|
||||
// @set public_fn_id = - "$.index[*][?(@.name=='public_fn')].id"
|
||||
// @has - "$.index[*][?(@.name=='public_fn')].id"
|
||||
pub fn public_fn();
|
||||
// @!has - "$.index[*][?(@.name=='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::*;
|
||||
|
@ -3,9 +3,11 @@
|
||||
#![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 {
|
||||
// @!has - "$.index[*][?(@.name=='mod2')]"
|
||||
// @is - "$.index[*][?(@.name=='mod2')].kind" \"module\"
|
||||
// @is - "$.index[*][?(@.name=='mod2')].inner.is_stripped" "true"
|
||||
mod mod2 {
|
||||
// @set m2pub_id = - "$.index[*][?(@.name=='Mod2Public')].id"
|
||||
pub struct Mod2Public;
|
||||
@ -13,15 +15,18 @@ mod mod1 {
|
||||
// @!has - "$.index[*][?(@.name=='Mod2Private')]"
|
||||
struct Mod2Private;
|
||||
}
|
||||
|
||||
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='mod2')]"
|
||||
pub use self::mod2::*;
|
||||
|
||||
// @set m1pub_id = - "$.index[*][?(@.name=='Mod1Public')].id"
|
||||
pub struct Mod1Public;
|
||||
|
||||
// @!has - "$.index[*][?(@.name=='Mod1Private')]"
|
||||
struct Mod1Private;
|
||||
}
|
||||
|
||||
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='mod1')]"
|
||||
pub use mod1::*;
|
||||
|
||||
// @has - "$.index[*][?(@.name=='glob_private')].inner.items[*]" $m2pub_id
|
||||
// @has - "$.index[*][?(@.name=='glob_private')].inner.items[*]" $m1pub_id
|
||||
// @has - "$.index[*][?(@.name=='mod2')].inner.items[*]" $m2pub_id
|
||||
// @has - "$.index[*][?(@.name=='mod1')].inner.items[*]" $m1pub_id
|
||||
|
@ -1,15 +1,17 @@
|
||||
#![feature(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 {
|
||||
// @set foo_id = in_root_and_mod.json "$.index[*][?(@.name=='Foo')].id"
|
||||
// @has - "$.index[*][?(@.name=='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 mod bar {
|
||||
// @has - "$.index[*][?(@.name=='bar')].inner.items[*]" $foo_id
|
||||
// @has - "$.index[*][?(@.kind=='import' && @.inner.source=='crate::foo::Foo')]"
|
||||
pub use crate::foo::Foo;
|
||||
}
|
||||
|
18
src/test/rustdoc-json/reexport/private_twice_one_inline.rs
Normal file
18
src/test/rustdoc-json/reexport/private_twice_one_inline.rs
Normal 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
|
17
src/test/rustdoc-json/reexport/private_two_names.rs
Normal file
17
src/test/rustdoc-json/reexport/private_two_names.rs
Normal 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;
|
@ -2,13 +2,13 @@
|
||||
|
||||
#![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 {
|
||||
// @!has - "$.index[*][?(@.name=='Public')]"
|
||||
// @has - "$.index[*][?(@.name=='Public')]"
|
||||
pub struct Public;
|
||||
}
|
||||
|
||||
// @set newname_id = - "$.index[*][?(@.name=='NewName')].id"
|
||||
// @is - "$.index[*][?(@.name=='NewName')].kind" \"struct\"
|
||||
// @has - "$.index[*][?(@.name=='rename_private')].inner.items[*]" $newname_id
|
||||
// @is - "$.index[*][?(@.kind=='import')].inner.name" \"NewName\"
|
||||
pub use inner::Public as NewName;
|
||||
|
@ -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)]
|
||||
#![no_std]
|
||||
#![no_core]
|
||||
|
||||
// @has same_type_reexported_more_than_once.json
|
||||
// @set trait_id = - "$.index[*][?(@.name=='Trait')].id"
|
||||
// @has - "$.index[*][?(@.name=='same_type_reexported_more_than_once')].inner.items[*]" $trait_id
|
||||
// @has - "$.index[*][?(@.name=='Trait')]"
|
||||
pub use inner::Trait;
|
||||
// @set reexport_id = - "$.index[*][?(@.name=='Reexport')].id"
|
||||
// @has - "$.index[*][?(@.name=='same_type_reexported_more_than_once')].inner.items[*]" $reexport_id
|
||||
// @has - "$.index[*].inner[?(@.name=='Reexport')].id"
|
||||
pub use inner::Trait as Reexport;
|
||||
|
||||
mod inner {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user