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
165 changed files with 2270 additions and 1215 deletions
|
@ -4239,6 +4239,7 @@ dependencies = [
|
||||||
"rustc_hir",
|
"rustc_hir",
|
||||||
"rustc_index",
|
"rustc_index",
|
||||||
"rustc_lexer",
|
"rustc_lexer",
|
||||||
|
"rustc_macros",
|
||||||
"rustc_middle",
|
"rustc_middle",
|
||||||
"rustc_serialize",
|
"rustc_serialize",
|
||||||
"rustc_session",
|
"rustc_session",
|
||||||
|
|
|
@ -7,7 +7,7 @@ use rustc_errors::{
|
||||||
};
|
};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
|
||||||
use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
|
use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_infer::traits::ObligationCause;
|
use rustc_infer::traits::ObligationCause;
|
||||||
|
@ -23,7 +23,7 @@ use rustc_middle::ty::{
|
||||||
use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
|
use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
|
||||||
use rustc_span::hygiene::DesugaringKind;
|
use rustc_span::hygiene::DesugaringKind;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::{BytePos, Span};
|
use rustc_span::{BytePos, Span, Symbol};
|
||||||
use rustc_trait_selection::infer::InferCtxtExt;
|
use rustc_trait_selection::infer::InferCtxtExt;
|
||||||
use rustc_trait_selection::traits::TraitEngineExt as _;
|
use rustc_trait_selection::traits::TraitEngineExt as _;
|
||||||
|
|
||||||
|
@ -1227,8 +1227,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
from_closure: false,
|
from_closure: false,
|
||||||
region_name:
|
region_name:
|
||||||
RegionName {
|
RegionName {
|
||||||
source:
|
source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
|
||||||
RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
|
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
|
@ -1500,7 +1499,70 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
| BorrowExplanation::UsedLaterInLoop(..)
|
| BorrowExplanation::UsedLaterInLoop(..)
|
||||||
| BorrowExplanation::UsedLaterWhenDropped { .. } => {
|
| BorrowExplanation::UsedLaterWhenDropped { .. } => {
|
||||||
// Only give this note and suggestion if it could be relevant.
|
// Only give this note and suggestion if it could be relevant.
|
||||||
err.note("consider using a `let` binding to create a longer lived value");
|
let sm = self.infcx.tcx.sess.source_map();
|
||||||
|
let mut suggested = false;
|
||||||
|
let msg = "consider using a `let` binding to create a longer lived value";
|
||||||
|
|
||||||
|
/// We check that there's a single level of block nesting to ensure always correct
|
||||||
|
/// suggestions. If we don't, then we only provide a free-form message to avoid
|
||||||
|
/// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`.
|
||||||
|
/// We could expand the analysis to suggest hoising all of the relevant parts of
|
||||||
|
/// the users' code to make the code compile, but that could be too much.
|
||||||
|
struct NestedStatementVisitor {
|
||||||
|
span: Span,
|
||||||
|
current: usize,
|
||||||
|
found: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
|
||||||
|
fn visit_block(&mut self, block: &hir::Block<'tcx>) {
|
||||||
|
self.current += 1;
|
||||||
|
walk_block(self, block);
|
||||||
|
self.current -= 1;
|
||||||
|
}
|
||||||
|
fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
|
||||||
|
if self.span == expr.span {
|
||||||
|
self.found = self.current;
|
||||||
|
}
|
||||||
|
walk_expr(self, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let source_info = self.body.source_info(location);
|
||||||
|
if let Some(scope) = self.body.source_scopes.get(source_info.scope)
|
||||||
|
&& let ClearCrossCrate::Set(scope_data) = &scope.local_data
|
||||||
|
&& let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root)
|
||||||
|
&& let Some(id) = node.body_id()
|
||||||
|
&& let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
|
||||||
|
{
|
||||||
|
for stmt in block.stmts {
|
||||||
|
let mut visitor = NestedStatementVisitor {
|
||||||
|
span: proper_span,
|
||||||
|
current: 0,
|
||||||
|
found: 0,
|
||||||
|
};
|
||||||
|
visitor.visit_stmt(stmt);
|
||||||
|
if visitor.found == 0
|
||||||
|
&& stmt.span.contains(proper_span)
|
||||||
|
&& let Some(p) = sm.span_to_margin(stmt.span)
|
||||||
|
&& let Ok(s) = sm.span_to_snippet(proper_span)
|
||||||
|
{
|
||||||
|
let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
|
||||||
|
err.multipart_suggestion_verbose(
|
||||||
|
msg,
|
||||||
|
vec![
|
||||||
|
(stmt.span.shrink_to_lo(), addition),
|
||||||
|
(proper_span, "binding".to_string()),
|
||||||
|
],
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
suggested = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !suggested {
|
||||||
|
err.note(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -1699,7 +1761,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
borrow_span: Span,
|
borrow_span: Span,
|
||||||
name: &Option<String>,
|
name: &Option<String>,
|
||||||
upvar_span: Span,
|
upvar_span: Span,
|
||||||
upvar_name: &str,
|
upvar_name: Symbol,
|
||||||
escape_span: Span,
|
escape_span: Span,
|
||||||
) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
|
) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
|
||||||
let tcx = self.infcx.tcx;
|
let tcx = self.infcx.tcx;
|
||||||
|
@ -2093,7 +2155,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
}
|
}
|
||||||
StorageDeadOrDrop::Destructor(_) => kind,
|
StorageDeadOrDrop::Destructor(_) => kind,
|
||||||
},
|
},
|
||||||
ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
|
ProjectionElem::OpaqueCast { .. }
|
||||||
|
| ProjectionElem::Field(..)
|
||||||
|
| ProjectionElem::Downcast(..) => {
|
||||||
match place_ty.ty.kind() {
|
match place_ty.ty.kind() {
|
||||||
ty::Adt(def, _) if def.has_dtor(tcx) => {
|
ty::Adt(def, _) if def.has_dtor(tcx) => {
|
||||||
// Report the outermost adt with a destructor
|
// Report the outermost adt with a destructor
|
||||||
|
|
|
@ -12,7 +12,7 @@ use rustc_middle::mir::{
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::adjustment::PointerCast;
|
use rustc_middle::ty::adjustment::PointerCast;
|
||||||
use rustc_middle::ty::{self, RegionVid, TyCtxt};
|
use rustc_middle::ty::{self, RegionVid, TyCtxt};
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::{kw, Symbol};
|
||||||
use rustc_span::{sym, DesugaringKind, Span};
|
use rustc_span::{sym, DesugaringKind, Span};
|
||||||
|
|
||||||
use crate::region_infer::BlameConstraint;
|
use crate::region_infer::BlameConstraint;
|
||||||
|
@ -282,7 +282,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
||||||
) {
|
) {
|
||||||
if let ConstraintCategory::OpaqueType = category {
|
if let ConstraintCategory::OpaqueType = category {
|
||||||
let suggestable_name =
|
let suggestable_name =
|
||||||
if region_name.was_named() { region_name.to_string() } else { "'_".to_string() };
|
if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime };
|
||||||
|
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"you can add a bound to the {}to make it last less than `'static` and match `{}`",
|
"you can add a bound to the {}to make it last less than `'static` and match `{}`",
|
||||||
|
|
|
@ -226,6 +226,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
}
|
}
|
||||||
ProjectionElem::Downcast(..) if including_downcast.0 => return None,
|
ProjectionElem::Downcast(..) if including_downcast.0 => return None,
|
||||||
ProjectionElem::Downcast(..) => (),
|
ProjectionElem::Downcast(..) => (),
|
||||||
|
ProjectionElem::OpaqueCast(..) => (),
|
||||||
ProjectionElem::Field(field, _ty) => {
|
ProjectionElem::Field(field, _ty) => {
|
||||||
// FIXME(project-rfc_2229#36): print capture precisely here.
|
// FIXME(project-rfc_2229#36): print capture precisely here.
|
||||||
if let Some(field) = self.is_upvar_field_projection(PlaceRef {
|
if let Some(field) = self.is_upvar_field_projection(PlaceRef {
|
||||||
|
@ -286,6 +287,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
|
PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
|
||||||
}
|
}
|
||||||
ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
|
ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
|
||||||
|
ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(*ty),
|
||||||
ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
|
ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -169,6 +169,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||||
..,
|
..,
|
||||||
ProjectionElem::Index(_)
|
ProjectionElem::Index(_)
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
|
| ProjectionElem::OpaqueCast { .. }
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
| ProjectionElem::Downcast(..),
|
| ProjectionElem::Downcast(..),
|
||||||
],
|
],
|
||||||
|
|
|
@ -19,8 +19,7 @@ use rustc_middle::ty::subst::InternalSubsts;
|
||||||
use rustc_middle::ty::Region;
|
use rustc_middle::ty::Region;
|
||||||
use rustc_middle::ty::TypeVisitor;
|
use rustc_middle::ty::TypeVisitor;
|
||||||
use rustc_middle::ty::{self, RegionVid, Ty};
|
use rustc_middle::ty::{self, RegionVid, Ty};
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::{kw, sym, Ident};
|
||||||
use rustc_span::symbol::Ident;
|
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
use crate::borrowck_errors;
|
use crate::borrowck_errors;
|
||||||
|
@ -758,7 +757,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let lifetime = if f.has_name() { fr_name.to_string() } else { "'_".to_string() };
|
let lifetime = if f.has_name() { fr_name.name } else { kw::UnderscoreLifetime };
|
||||||
|
|
||||||
let arg = match param.param.pat.simple_ident() {
|
let arg = match param.param.pat.simple_ident() {
|
||||||
Some(simple_ident) => format!("argument `{}`", simple_ident),
|
Some(simple_ident) => format!("argument `{}`", simple_ident),
|
||||||
|
@ -770,7 +769,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||||
self.infcx.tcx,
|
self.infcx.tcx,
|
||||||
diag,
|
diag,
|
||||||
fn_returns,
|
fn_returns,
|
||||||
lifetime,
|
lifetime.to_string(),
|
||||||
Some(arg),
|
Some(arg),
|
||||||
captures,
|
captures,
|
||||||
Some((param.param_ty_span, param.param_ty.to_string())),
|
Some((param.param_ty_span, param.param_ty.to_string())),
|
||||||
|
|
|
@ -34,13 +34,13 @@ pub(crate) enum RegionNameSource {
|
||||||
/// The `'static` region.
|
/// The `'static` region.
|
||||||
Static,
|
Static,
|
||||||
/// The free region corresponding to the environment of a closure.
|
/// The free region corresponding to the environment of a closure.
|
||||||
SynthesizedFreeEnvRegion(Span, String),
|
SynthesizedFreeEnvRegion(Span, &'static str),
|
||||||
/// The region corresponding to an argument.
|
/// The region corresponding to an argument.
|
||||||
AnonRegionFromArgument(RegionNameHighlight),
|
AnonRegionFromArgument(RegionNameHighlight),
|
||||||
/// The region corresponding to a closure upvar.
|
/// The region corresponding to a closure upvar.
|
||||||
AnonRegionFromUpvar(Span, String),
|
AnonRegionFromUpvar(Span, Symbol),
|
||||||
/// The region corresponding to the return type of a closure.
|
/// The region corresponding to the return type of a closure.
|
||||||
AnonRegionFromOutput(RegionNameHighlight, String),
|
AnonRegionFromOutput(RegionNameHighlight, &'static str),
|
||||||
/// The region from a type yielded by a generator.
|
/// The region from a type yielded by a generator.
|
||||||
AnonRegionFromYieldTy(Span, String),
|
AnonRegionFromYieldTy(Span, String),
|
||||||
/// An anonymous region from an async fn.
|
/// An anonymous region from an async fn.
|
||||||
|
@ -110,7 +110,7 @@ impl RegionName {
|
||||||
}
|
}
|
||||||
RegionNameSource::SynthesizedFreeEnvRegion(span, note) => {
|
RegionNameSource::SynthesizedFreeEnvRegion(span, note) => {
|
||||||
diag.span_label(*span, format!("lifetime `{self}` represents this closure's body"));
|
diag.span_label(*span, format!("lifetime `{self}` represents this closure's body"));
|
||||||
diag.note(note);
|
diag.note(*note);
|
||||||
}
|
}
|
||||||
RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy(
|
RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy(
|
||||||
span,
|
span,
|
||||||
|
@ -350,10 +350,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||||
|
|
||||||
Some(RegionName {
|
Some(RegionName {
|
||||||
name: region_name,
|
name: region_name,
|
||||||
source: RegionNameSource::SynthesizedFreeEnvRegion(
|
source: RegionNameSource::SynthesizedFreeEnvRegion(fn_decl_span, note),
|
||||||
fn_decl_span,
|
|
||||||
note.to_string(),
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -678,7 +675,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||||
|
|
||||||
Some(RegionName {
|
Some(RegionName {
|
||||||
name: region_name,
|
name: region_name,
|
||||||
source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name.to_string()),
|
source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -756,7 +753,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||||
|
|
||||||
Some(RegionName {
|
Some(RegionName {
|
||||||
name: self.synthesize_region_name(),
|
name: self.synthesize_region_name(),
|
||||||
source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description.to_string()),
|
source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1788,6 +1788,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
for (place_base, elem) in place.iter_projections().rev() {
|
for (place_base, elem) in place.iter_projections().rev() {
|
||||||
match elem {
|
match elem {
|
||||||
ProjectionElem::Index(_/*operand*/) |
|
ProjectionElem::Index(_/*operand*/) |
|
||||||
|
ProjectionElem::OpaqueCast(_) |
|
||||||
ProjectionElem::ConstantIndex { .. } |
|
ProjectionElem::ConstantIndex { .. } |
|
||||||
// assigning to P[i] requires P to be valid.
|
// assigning to P[i] requires P to be valid.
|
||||||
ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
|
ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
|
||||||
|
@ -2179,6 +2180,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
| ProjectionElem::Index(..)
|
| ProjectionElem::Index(..)
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
|
| ProjectionElem::OpaqueCast { .. }
|
||||||
| ProjectionElem::Downcast(..) => {
|
| ProjectionElem::Downcast(..) => {
|
||||||
let upvar_field_projection = self.is_upvar_field_projection(place);
|
let upvar_field_projection = self.is_upvar_field_projection(place);
|
||||||
if let Some(field) = upvar_field_projection {
|
if let Some(field) = upvar_field_projection {
|
||||||
|
|
|
@ -255,6 +255,7 @@ fn place_components_conflict<'tcx>(
|
||||||
| (ProjectionElem::Index { .. }, _, _)
|
| (ProjectionElem::Index { .. }, _, _)
|
||||||
| (ProjectionElem::ConstantIndex { .. }, _, _)
|
| (ProjectionElem::ConstantIndex { .. }, _, _)
|
||||||
| (ProjectionElem::Subslice { .. }, _, _)
|
| (ProjectionElem::Subslice { .. }, _, _)
|
||||||
|
| (ProjectionElem::OpaqueCast { .. }, _, _)
|
||||||
| (ProjectionElem::Downcast { .. }, _, _) => {
|
| (ProjectionElem::Downcast { .. }, _, _) => {
|
||||||
// Recursive case. This can still be disjoint on a
|
// Recursive case. This can still be disjoint on a
|
||||||
// further iteration if this a shallow access and
|
// further iteration if this a shallow access and
|
||||||
|
@ -322,6 +323,17 @@ fn place_projection_conflict<'tcx>(
|
||||||
debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
|
debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
|
||||||
Overlap::EqualOrDisjoint
|
Overlap::EqualOrDisjoint
|
||||||
}
|
}
|
||||||
|
(ProjectionElem::OpaqueCast(v1), ProjectionElem::OpaqueCast(v2)) => {
|
||||||
|
if v1 == v2 {
|
||||||
|
// same type - recur.
|
||||||
|
debug!("place_element_conflict: DISJOINT-OR-EQ-OPAQUE");
|
||||||
|
Overlap::EqualOrDisjoint
|
||||||
|
} else {
|
||||||
|
// Different types. Disjoint!
|
||||||
|
debug!("place_element_conflict: DISJOINT-OPAQUE");
|
||||||
|
Overlap::Disjoint
|
||||||
|
}
|
||||||
|
}
|
||||||
(ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
|
(ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
|
||||||
if f1 == f2 {
|
if f1 == f2 {
|
||||||
// same field (e.g., `a.y` vs. `a.y`) - recur.
|
// same field (e.g., `a.y` vs. `a.y`) - recur.
|
||||||
|
@ -525,6 +537,7 @@ fn place_projection_conflict<'tcx>(
|
||||||
| ProjectionElem::Field(..)
|
| ProjectionElem::Field(..)
|
||||||
| ProjectionElem::Index(..)
|
| ProjectionElem::Index(..)
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
|
| ProjectionElem::OpaqueCast { .. }
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
| ProjectionElem::Downcast(..),
|
| ProjectionElem::Downcast(..),
|
||||||
_,
|
_,
|
||||||
|
|
|
@ -81,6 +81,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
|
||||||
}
|
}
|
||||||
ProjectionElem::Downcast(..)
|
ProjectionElem::Downcast(..)
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
|
| ProjectionElem::OpaqueCast { .. }
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
| ProjectionElem::Index(_) => {
|
| ProjectionElem::Index(_) => {
|
||||||
cursor = cursor_base;
|
cursor = cursor_base;
|
||||||
|
|
|
@ -790,6 +790,19 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||||
}
|
}
|
||||||
PlaceTy::from_ty(fty)
|
PlaceTy::from_ty(fty)
|
||||||
}
|
}
|
||||||
|
ProjectionElem::OpaqueCast(ty) => {
|
||||||
|
let ty = self.sanitize_type(place, ty);
|
||||||
|
let ty = self.cx.normalize(ty, location);
|
||||||
|
self.cx
|
||||||
|
.eq_types(
|
||||||
|
base.ty,
|
||||||
|
ty,
|
||||||
|
location.to_locations(),
|
||||||
|
ConstraintCategory::TypeAnnotation,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
PlaceTy::from_ty(ty)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1195,10 +1208,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
tcx,
|
tcx,
|
||||||
self.param_env,
|
self.param_env,
|
||||||
proj,
|
proj,
|
||||||
|this, field, ()| {
|
|this, field, _| {
|
||||||
let ty = this.field_ty(tcx, field);
|
let ty = this.field_ty(tcx, field);
|
||||||
self.normalize(ty, locations)
|
self.normalize(ty, locations)
|
||||||
},
|
},
|
||||||
|
|_, _| unreachable!(),
|
||||||
);
|
);
|
||||||
curr_projected_ty = projected_ty;
|
curr_projected_ty = projected_ty;
|
||||||
}
|
}
|
||||||
|
@ -2493,6 +2507,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
ProjectionElem::Field(..)
|
ProjectionElem::Field(..)
|
||||||
| ProjectionElem::Downcast(..)
|
| ProjectionElem::Downcast(..)
|
||||||
|
| ProjectionElem::OpaqueCast(..)
|
||||||
| ProjectionElem::Index(..)
|
| ProjectionElem::Index(..)
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
| ProjectionElem::Subslice { .. } => {
|
| ProjectionElem::Subslice { .. } => {
|
||||||
|
|
|
@ -825,6 +825,7 @@ pub(crate) fn codegen_place<'tcx>(
|
||||||
cplace = cplace.place_deref(fx);
|
cplace = cplace.place_deref(fx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PlaceElem::OpaqueCast(ty) => cplace = cplace.place_opaque_cast(fx, ty),
|
||||||
PlaceElem::Field(field, _ty) => {
|
PlaceElem::Field(field, _ty) => {
|
||||||
cplace = cplace.place_field(fx, field);
|
cplace = cplace.place_field(fx, field);
|
||||||
}
|
}
|
||||||
|
|
|
@ -615,6 +615,14 @@ impl<'tcx> CPlace<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn place_opaque_cast(
|
||||||
|
self,
|
||||||
|
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||||
|
ty: Ty<'tcx>,
|
||||||
|
) -> CPlace<'tcx> {
|
||||||
|
CPlace { inner: self.inner, layout: fx.layout_of(ty) }
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn place_field(
|
pub(crate) fn place_field(
|
||||||
self,
|
self,
|
||||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||||
|
|
|
@ -411,6 +411,21 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
||||||
downcast
|
downcast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn project_type<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
|
||||||
|
&self,
|
||||||
|
bx: &mut Bx,
|
||||||
|
ty: Ty<'tcx>,
|
||||||
|
) -> Self {
|
||||||
|
let mut downcast = *self;
|
||||||
|
downcast.layout = bx.cx().layout_of(ty);
|
||||||
|
|
||||||
|
// Cast to the appropriate type.
|
||||||
|
let variant_ty = bx.cx().backend_type(downcast.layout);
|
||||||
|
downcast.llval = bx.pointercast(downcast.llval, bx.cx().type_ptr_to(variant_ty));
|
||||||
|
|
||||||
|
downcast
|
||||||
|
}
|
||||||
|
|
||||||
pub fn storage_live<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx) {
|
pub fn storage_live<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx) {
|
||||||
bx.lifetime_start(self.llval, self.layout.size);
|
bx.lifetime_start(self.llval, self.layout.size);
|
||||||
}
|
}
|
||||||
|
@ -459,6 +474,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
mir::ProjectionElem::Field(ref field, _) => {
|
mir::ProjectionElem::Field(ref field, _) => {
|
||||||
cg_base.project_field(bx, field.index())
|
cg_base.project_field(bx, field.index())
|
||||||
}
|
}
|
||||||
|
mir::ProjectionElem::OpaqueCast(ty) => cg_base.project_type(bx, ty),
|
||||||
mir::ProjectionElem::Index(index) => {
|
mir::ProjectionElem::Index(index) => {
|
||||||
let index = &mir::Operand::Copy(mir::Place::from(index));
|
let index = &mir::Operand::Copy(mir::Place::from(index));
|
||||||
let index = self.codegen_operand(bx, index);
|
let index = self.codegen_operand(bx, index);
|
||||||
|
|
|
@ -436,7 +436,7 @@ fn valtree_into_mplace<'tcx>(
|
||||||
|
|
||||||
let offset = place_adjusted.layout.fields.offset(i);
|
let offset = place_adjusted.layout.fields.offset(i);
|
||||||
place
|
place
|
||||||
.offset(
|
.offset_with_meta(
|
||||||
offset,
|
offset,
|
||||||
MemPlaceMeta::Meta(Scalar::from_machine_usize(
|
MemPlaceMeta::Meta(Scalar::from_machine_usize(
|
||||||
num_elems as u64,
|
num_elems as u64,
|
||||||
|
|
|
@ -297,7 +297,7 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset(
|
pub fn offset_with_meta(
|
||||||
&self,
|
&self,
|
||||||
offset: Size,
|
offset: Size,
|
||||||
meta: MemPlaceMeta<Tag>,
|
meta: MemPlaceMeta<Tag>,
|
||||||
|
@ -305,7 +305,7 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> {
|
||||||
cx: &impl HasDataLayout,
|
cx: &impl HasDataLayout,
|
||||||
) -> InterpResult<'tcx, Self> {
|
) -> InterpResult<'tcx, Self> {
|
||||||
match self.try_as_mplace() {
|
match self.try_as_mplace() {
|
||||||
Ok(mplace) => Ok(mplace.offset(offset, meta, layout, cx)?.into()),
|
Ok(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, cx)?.into()),
|
||||||
Err(imm) => {
|
Err(imm) => {
|
||||||
assert!(
|
assert!(
|
||||||
matches!(*imm, Immediate::Uninit),
|
matches!(*imm, Immediate::Uninit),
|
||||||
|
@ -317,6 +317,16 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn offset(
|
||||||
|
&self,
|
||||||
|
offset: Size,
|
||||||
|
layout: TyAndLayout<'tcx>,
|
||||||
|
cx: &impl HasDataLayout,
|
||||||
|
) -> InterpResult<'tcx, Self> {
|
||||||
|
assert!(!layout.is_unsized());
|
||||||
|
self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
|
|
|
@ -163,7 +163,7 @@ impl<Tag: Provenance> MemPlace<Tag> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn offset<'tcx>(
|
pub fn offset_with_meta<'tcx>(
|
||||||
self,
|
self,
|
||||||
offset: Size,
|
offset: Size,
|
||||||
meta: MemPlaceMeta<Tag>,
|
meta: MemPlaceMeta<Tag>,
|
||||||
|
@ -199,7 +199,7 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn offset(
|
pub fn offset_with_meta(
|
||||||
&self,
|
&self,
|
||||||
offset: Size,
|
offset: Size,
|
||||||
meta: MemPlaceMeta<Tag>,
|
meta: MemPlaceMeta<Tag>,
|
||||||
|
@ -207,12 +207,22 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> {
|
||||||
cx: &impl HasDataLayout,
|
cx: &impl HasDataLayout,
|
||||||
) -> InterpResult<'tcx, Self> {
|
) -> InterpResult<'tcx, Self> {
|
||||||
Ok(MPlaceTy {
|
Ok(MPlaceTy {
|
||||||
mplace: self.mplace.offset(offset, meta, cx)?,
|
mplace: self.mplace.offset_with_meta(offset, meta, cx)?,
|
||||||
align: self.align.restrict_for_offset(offset),
|
align: self.align.restrict_for_offset(offset),
|
||||||
layout,
|
layout,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn offset(
|
||||||
|
&self,
|
||||||
|
offset: Size,
|
||||||
|
layout: TyAndLayout<'tcx>,
|
||||||
|
cx: &impl HasDataLayout,
|
||||||
|
) -> InterpResult<'tcx, Self> {
|
||||||
|
assert!(!layout.is_unsized());
|
||||||
|
self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_aligned_ptr(ptr: Pointer<Option<Tag>>, layout: TyAndLayout<'tcx>) -> Self {
|
pub fn from_aligned_ptr(ptr: Pointer<Option<Tag>>, layout: TyAndLayout<'tcx>) -> Self {
|
||||||
MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi }
|
MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi }
|
||||||
|
|
|
@ -63,7 +63,7 @@ where
|
||||||
|
|
||||||
// We do not look at `base.layout.align` nor `field_layout.align`, unlike
|
// We do not look at `base.layout.align` nor `field_layout.align`, unlike
|
||||||
// codegen -- mostly to see if we can get away with that
|
// codegen -- mostly to see if we can get away with that
|
||||||
base.offset(offset, meta, field_layout, self)
|
base.offset_with_meta(offset, meta, field_layout, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the place of a field inside the place, and also the field's type.
|
/// Gets the place of a field inside the place, and also the field's type.
|
||||||
|
@ -193,9 +193,7 @@ where
|
||||||
let offset = stride * index; // `Size` multiplication
|
let offset = stride * index; // `Size` multiplication
|
||||||
// All fields have the same layout.
|
// All fields have the same layout.
|
||||||
let field_layout = base.layout.field(self, 0);
|
let field_layout = base.layout.field(self, 0);
|
||||||
assert!(!field_layout.is_unsized());
|
base.offset(offset, field_layout, self)
|
||||||
|
|
||||||
base.offset(offset, MemPlaceMeta::None, field_layout, self)
|
|
||||||
}
|
}
|
||||||
_ => span_bug!(
|
_ => span_bug!(
|
||||||
self.cur_span(),
|
self.cur_span(),
|
||||||
|
@ -215,10 +213,10 @@ where
|
||||||
let abi::FieldsShape::Array { stride, .. } = base.layout.fields else {
|
let abi::FieldsShape::Array { stride, .. } = base.layout.fields else {
|
||||||
span_bug!(self.cur_span(), "operand_array_fields: expected an array layout");
|
span_bug!(self.cur_span(), "operand_array_fields: expected an array layout");
|
||||||
};
|
};
|
||||||
let layout = base.layout.field(self, 0);
|
let field_layout = base.layout.field(self, 0);
|
||||||
let dl = &self.tcx.data_layout;
|
let dl = &self.tcx.data_layout;
|
||||||
// `Size` multiplication
|
// `Size` multiplication
|
||||||
Ok((0..len).map(move |i| base.offset(stride * i, MemPlaceMeta::None, layout, dl)))
|
Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Index into an array.
|
/// Index into an array.
|
||||||
|
@ -326,7 +324,7 @@ where
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let layout = self.layout_of(ty)?;
|
let layout = self.layout_of(ty)?;
|
||||||
base.offset(from_offset, meta, layout, self)
|
base.offset_with_meta(from_offset, meta, layout, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn place_subslice(
|
pub fn place_subslice(
|
||||||
|
@ -351,6 +349,11 @@ where
|
||||||
) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
|
) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
|
||||||
use rustc_middle::mir::ProjectionElem::*;
|
use rustc_middle::mir::ProjectionElem::*;
|
||||||
Ok(match proj_elem {
|
Ok(match proj_elem {
|
||||||
|
OpaqueCast(ty) => {
|
||||||
|
let mut place = *base;
|
||||||
|
place.layout = self.layout_of(ty)?;
|
||||||
|
place
|
||||||
|
}
|
||||||
Field(field, _) => self.place_field(base, field.index())?,
|
Field(field, _) => self.place_field(base, field.index())?,
|
||||||
Downcast(_, variant) => self.place_downcast(base, variant)?,
|
Downcast(_, variant) => self.place_downcast(base, variant)?,
|
||||||
Deref => self.deref_operand(&self.place_to_op(base)?)?.into(),
|
Deref => self.deref_operand(&self.place_to_op(base)?)?.into(),
|
||||||
|
@ -375,6 +378,11 @@ where
|
||||||
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
||||||
use rustc_middle::mir::ProjectionElem::*;
|
use rustc_middle::mir::ProjectionElem::*;
|
||||||
Ok(match proj_elem {
|
Ok(match proj_elem {
|
||||||
|
OpaqueCast(ty) => {
|
||||||
|
let mut op = *base;
|
||||||
|
op.layout = self.layout_of(ty)?;
|
||||||
|
op
|
||||||
|
}
|
||||||
Field(field, _) => self.operand_field(base, field.index())?,
|
Field(field, _) => self.operand_field(base, field.index())?,
|
||||||
Downcast(_, variant) => self.operand_downcast(base, variant)?,
|
Downcast(_, variant) => self.operand_downcast(base, variant)?,
|
||||||
Deref => self.deref_operand(base)?.into(),
|
Deref => self.deref_operand(base)?.into(),
|
||||||
|
|
|
@ -853,7 +853,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
||||||
self.visit_scalar(scalar, scalar_layout)?;
|
self.visit_scalar(scalar, scalar_layout)?;
|
||||||
}
|
}
|
||||||
Abi::ScalarPair(a_layout, b_layout) => {
|
Abi::ScalarPair(a_layout, b_layout) => {
|
||||||
// We would validate these things as we descend into the fields,
|
// There is no `rustc_layout_scalar_valid_range_start` for pairs, so
|
||||||
|
// we would validate these things as we descend into the fields,
|
||||||
// but that can miss bugs in layout computation. Layout computation
|
// but that can miss bugs in layout computation. Layout computation
|
||||||
// is subtle due to enums having ScalarPair layout, where one field
|
// is subtle due to enums having ScalarPair layout, where one field
|
||||||
// is the discriminant.
|
// is the discriminant.
|
||||||
|
@ -867,7 +868,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
||||||
}
|
}
|
||||||
Abi::Vector { .. } => {
|
Abi::Vector { .. } => {
|
||||||
// No checks here, we assume layout computation gets this right.
|
// No checks here, we assume layout computation gets this right.
|
||||||
// (This is harder to check since Miri does not represent these as `Immediate`.)
|
// (This is harder to check since Miri does not represent these as `Immediate`. We
|
||||||
|
// also cannot use field projections since this might be a newtype around a vector.)
|
||||||
}
|
}
|
||||||
Abi::Aggregate { .. } => {
|
Abi::Aggregate { .. } => {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
|
|
|
@ -8,23 +8,33 @@ use rustc_target::abi::{FieldsShape, VariantIdx, Variants};
|
||||||
|
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
use super::{InterpCx, MPlaceTy, Machine, OpTy};
|
use super::{InterpCx, MPlaceTy, Machine, OpTy, PlaceTy};
|
||||||
|
|
||||||
// A thing that we can project into, and that has a layout.
|
/// A thing that we can project into, and that has a layout.
|
||||||
// This wouldn't have to depend on `Machine` but with the current type inference,
|
/// This wouldn't have to depend on `Machine` but with the current type inference,
|
||||||
// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
|
/// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
|
||||||
pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy {
|
pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy {
|
||||||
/// Gets this value's layout.
|
/// Gets this value's layout.
|
||||||
fn layout(&self) -> TyAndLayout<'tcx>;
|
fn layout(&self) -> TyAndLayout<'tcx>;
|
||||||
|
|
||||||
/// Makes this into an `OpTy`.
|
/// Makes this into an `OpTy`, in a cheap way that is good for reading.
|
||||||
fn to_op(&self, ecx: &InterpCx<'mir, 'tcx, M>)
|
fn to_op_for_read(
|
||||||
-> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>;
|
&self,
|
||||||
|
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
|
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>;
|
||||||
|
|
||||||
|
/// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections.
|
||||||
|
fn to_op_for_proj(
|
||||||
|
&self,
|
||||||
|
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
|
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
||||||
|
self.to_op_for_read(ecx)
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates this from an `OpTy`.
|
/// Creates this from an `OpTy`.
|
||||||
///
|
///
|
||||||
/// If `to_op` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
|
/// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
|
||||||
fn from_op(mplace: OpTy<'tcx, M::PointerTag>) -> Self;
|
fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self;
|
||||||
|
|
||||||
/// Projects to the given enum variant.
|
/// Projects to the given enum variant.
|
||||||
fn project_downcast(
|
fn project_downcast(
|
||||||
|
@ -41,8 +51,50 @@ pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy {
|
||||||
) -> InterpResult<'tcx, Self>;
|
) -> InterpResult<'tcx, Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operands and memory-places are both values.
|
/// A thing that we can project into given *mutable* access to `ecx`, and that has a layout.
|
||||||
// Places in general are not due to `place_field` having to do `force_allocation`.
|
/// This wouldn't have to depend on `Machine` but with the current type inference,
|
||||||
|
/// that's just more convenient to work with (avoids repeating all the `Machine` bounds).
|
||||||
|
pub trait ValueMut<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy {
|
||||||
|
/// Gets this value's layout.
|
||||||
|
fn layout(&self) -> TyAndLayout<'tcx>;
|
||||||
|
|
||||||
|
/// Makes this into an `OpTy`, in a cheap way that is good for reading.
|
||||||
|
fn to_op_for_read(
|
||||||
|
&self,
|
||||||
|
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
|
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>;
|
||||||
|
|
||||||
|
/// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections.
|
||||||
|
fn to_op_for_proj(
|
||||||
|
&self,
|
||||||
|
ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||||
|
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>;
|
||||||
|
|
||||||
|
/// Creates this from an `OpTy`.
|
||||||
|
///
|
||||||
|
/// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
|
||||||
|
fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self;
|
||||||
|
|
||||||
|
/// Projects to the given enum variant.
|
||||||
|
fn project_downcast(
|
||||||
|
&self,
|
||||||
|
ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||||
|
variant: VariantIdx,
|
||||||
|
) -> InterpResult<'tcx, Self>;
|
||||||
|
|
||||||
|
/// Projects to the n-th field.
|
||||||
|
fn project_field(
|
||||||
|
&self,
|
||||||
|
ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||||
|
field: usize,
|
||||||
|
) -> InterpResult<'tcx, Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We cannot have a general impl which shows that Value implies ValueMut. (When we do, it says we
|
||||||
|
// cannot `impl ValueMut for PlaceTy` because some downstream crate could `impl Value for PlaceTy`.)
|
||||||
|
// So we have some copy-paste here. (We could have a macro but since we only have 2 types with this
|
||||||
|
// double-impl, that would barely make the code shorter, if at all.)
|
||||||
|
|
||||||
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> {
|
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn layout(&self) -> TyAndLayout<'tcx> {
|
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||||
|
@ -50,7 +102,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn to_op(
|
fn to_op_for_read(
|
||||||
&self,
|
&self,
|
||||||
_ecx: &InterpCx<'mir, 'tcx, M>,
|
_ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
||||||
|
@ -58,8 +110,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from_op(op: OpTy<'tcx, M::PointerTag>) -> Self {
|
fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self {
|
||||||
op
|
*op
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -81,6 +133,54 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
|
||||||
|
for OpTy<'tcx, M::PointerTag>
|
||||||
|
{
|
||||||
|
#[inline(always)]
|
||||||
|
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||||
|
self.layout
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn to_op_for_read(
|
||||||
|
&self,
|
||||||
|
_ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
|
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
||||||
|
Ok(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn to_op_for_proj(
|
||||||
|
&self,
|
||||||
|
_ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||||
|
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
||||||
|
Ok(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self {
|
||||||
|
*op
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn project_downcast(
|
||||||
|
&self,
|
||||||
|
ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||||
|
variant: VariantIdx,
|
||||||
|
) -> InterpResult<'tcx, Self> {
|
||||||
|
ecx.operand_downcast(self, variant)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn project_field(
|
||||||
|
&self,
|
||||||
|
ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||||
|
field: usize,
|
||||||
|
) -> InterpResult<'tcx, Self> {
|
||||||
|
ecx.operand_field(self, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
|
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
|
||||||
for MPlaceTy<'tcx, M::PointerTag>
|
for MPlaceTy<'tcx, M::PointerTag>
|
||||||
{
|
{
|
||||||
|
@ -90,7 +190,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn to_op(
|
fn to_op_for_read(
|
||||||
&self,
|
&self,
|
||||||
_ecx: &InterpCx<'mir, 'tcx, M>,
|
_ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
||||||
|
@ -98,8 +198,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from_op(op: OpTy<'tcx, M::PointerTag>) -> Self {
|
fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self {
|
||||||
// assert is justified because our `to_op` only ever produces `Indirect` operands.
|
// assert is justified because our `to_op_for_read` only ever produces `Indirect` operands.
|
||||||
op.assert_mem_place()
|
op.assert_mem_place()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,11 +222,111 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
|
||||||
|
for MPlaceTy<'tcx, M::PointerTag>
|
||||||
|
{
|
||||||
|
#[inline(always)]
|
||||||
|
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||||
|
self.layout
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn to_op_for_read(
|
||||||
|
&self,
|
||||||
|
_ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
|
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
||||||
|
Ok(self.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn to_op_for_proj(
|
||||||
|
&self,
|
||||||
|
_ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||||
|
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
||||||
|
Ok(self.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self {
|
||||||
|
// assert is justified because our `to_op_for_proj` only ever produces `Indirect` operands.
|
||||||
|
op.assert_mem_place()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn project_downcast(
|
||||||
|
&self,
|
||||||
|
ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||||
|
variant: VariantIdx,
|
||||||
|
) -> InterpResult<'tcx, Self> {
|
||||||
|
ecx.mplace_downcast(self, variant)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn project_field(
|
||||||
|
&self,
|
||||||
|
ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||||
|
field: usize,
|
||||||
|
) -> InterpResult<'tcx, Self> {
|
||||||
|
ecx.mplace_field(self, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
|
||||||
|
for PlaceTy<'tcx, M::PointerTag>
|
||||||
|
{
|
||||||
|
#[inline(always)]
|
||||||
|
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||||
|
self.layout
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn to_op_for_read(
|
||||||
|
&self,
|
||||||
|
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
|
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
||||||
|
// We `force_allocation` here so that `from_op` below can work.
|
||||||
|
ecx.place_to_op(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn to_op_for_proj(
|
||||||
|
&self,
|
||||||
|
ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||||
|
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
|
||||||
|
// We `force_allocation` here so that `from_op` below can work.
|
||||||
|
Ok(ecx.force_allocation(self)?.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self {
|
||||||
|
// assert is justified because our `to_op` only ever produces `Indirect` operands.
|
||||||
|
op.assert_mem_place().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn project_downcast(
|
||||||
|
&self,
|
||||||
|
ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||||
|
variant: VariantIdx,
|
||||||
|
) -> InterpResult<'tcx, Self> {
|
||||||
|
ecx.place_downcast(self, variant)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn project_field(
|
||||||
|
&self,
|
||||||
|
ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||||
|
field: usize,
|
||||||
|
) -> InterpResult<'tcx, Self> {
|
||||||
|
ecx.place_field(self, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! make_value_visitor {
|
macro_rules! make_value_visitor {
|
||||||
($visitor_trait_name:ident, $($mutability:ident)?) => {
|
($visitor_trait:ident, $value_trait:ident, $($mutability:ident)?) => {
|
||||||
// How to traverse a value and what to do when we are at the leaves.
|
// How to traverse a value and what to do when we are at the leaves.
|
||||||
pub trait $visitor_trait_name<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
|
pub trait $visitor_trait<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
|
||||||
type V: Value<'mir, 'tcx, M>;
|
type V: $value_trait<'mir, 'tcx, M>;
|
||||||
|
|
||||||
/// The visitor must have an `InterpCx` in it.
|
/// The visitor must have an `InterpCx` in it.
|
||||||
fn ecx(&$($mutability)? self)
|
fn ecx(&$($mutability)? self)
|
||||||
|
@ -215,19 +415,20 @@ macro_rules! make_value_visitor {
|
||||||
}
|
}
|
||||||
fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx>
|
fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx>
|
||||||
{
|
{
|
||||||
trace!("walk_value: type: {}", v.layout().ty);
|
let ty = v.layout().ty;
|
||||||
|
trace!("walk_value: type: {ty}");
|
||||||
|
|
||||||
// Special treatment for special types, where the (static) layout is not sufficient.
|
// Special treatment for special types, where the (static) layout is not sufficient.
|
||||||
match *v.layout().ty.kind() {
|
match *ty.kind() {
|
||||||
// If it is a trait object, switch to the real type that was used to create it.
|
// If it is a trait object, switch to the real type that was used to create it.
|
||||||
ty::Dynamic(..) => {
|
ty::Dynamic(..) => {
|
||||||
// unsized values are never immediate, so we can assert_mem_place
|
// unsized values are never immediate, so we can assert_mem_place
|
||||||
let op = v.to_op(self.ecx())?;
|
let op = v.to_op_for_read(self.ecx())?;
|
||||||
let dest = op.assert_mem_place();
|
let dest = op.assert_mem_place();
|
||||||
let inner = self.ecx().unpack_dyn_trait(&dest)?.1;
|
let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.1;
|
||||||
trace!("walk_value: dyn object layout: {:#?}", inner.layout);
|
trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
|
||||||
// recurse with the inner type
|
// recurse with the inner type
|
||||||
return self.visit_field(&v, 0, &Value::from_op(inner.into()));
|
return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into()));
|
||||||
},
|
},
|
||||||
// Slices do not need special handling here: they have `Array` field
|
// Slices do not need special handling here: they have `Array` field
|
||||||
// placement with length 0, so we enter the `Array` case below which
|
// placement with length 0, so we enter the `Array` case below which
|
||||||
|
@ -278,10 +479,10 @@ macro_rules! make_value_visitor {
|
||||||
|
|
||||||
// Visit the fields of this value.
|
// Visit the fields of this value.
|
||||||
match v.layout().fields {
|
match v.layout().fields {
|
||||||
FieldsShape::Primitive => {},
|
FieldsShape::Primitive => {}
|
||||||
FieldsShape::Union(fields) => {
|
FieldsShape::Union(fields) => {
|
||||||
self.visit_union(v, fields)?;
|
self.visit_union(v, fields)?;
|
||||||
},
|
}
|
||||||
FieldsShape::Arbitrary { ref offsets, .. } => {
|
FieldsShape::Arbitrary { ref offsets, .. } => {
|
||||||
// FIXME: We collect in a vec because otherwise there are lifetime
|
// FIXME: We collect in a vec because otherwise there are lifetime
|
||||||
// errors: Projecting to a field needs access to `ecx`.
|
// errors: Projecting to a field needs access to `ecx`.
|
||||||
|
@ -291,16 +492,17 @@ macro_rules! make_value_visitor {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
self.visit_aggregate(v, fields.into_iter())?;
|
self.visit_aggregate(v, fields.into_iter())?;
|
||||||
},
|
}
|
||||||
FieldsShape::Array { .. } => {
|
FieldsShape::Array { .. } => {
|
||||||
// Let's get an mplace first.
|
// Let's get an mplace (or immediate) first.
|
||||||
let op = v.to_op(self.ecx())?;
|
// This might `force_allocate` if `v` is a `PlaceTy`, but `place_index` does that anyway.
|
||||||
|
let op = v.to_op_for_proj(self.ecx())?;
|
||||||
// Now we can go over all the fields.
|
// Now we can go over all the fields.
|
||||||
// This uses the *run-time length*, i.e., if we are a slice,
|
// This uses the *run-time length*, i.e., if we are a slice,
|
||||||
// the dynamic info from the metadata is used.
|
// the dynamic info from the metadata is used.
|
||||||
let iter = self.ecx().operand_array_fields(&op)?
|
let iter = self.ecx().operand_array_fields(&op)?
|
||||||
.map(|f| f.and_then(|f| {
|
.map(|f| f.and_then(|f| {
|
||||||
Ok(Value::from_op(f))
|
Ok($value_trait::from_op(&f))
|
||||||
}));
|
}));
|
||||||
self.visit_aggregate(v, iter)?;
|
self.visit_aggregate(v, iter)?;
|
||||||
}
|
}
|
||||||
|
@ -310,7 +512,7 @@ macro_rules! make_value_visitor {
|
||||||
// If this is a multi-variant layout, find the right variant and proceed
|
// If this is a multi-variant layout, find the right variant and proceed
|
||||||
// with *its* fields.
|
// with *its* fields.
|
||||||
Variants::Multiple { .. } => {
|
Variants::Multiple { .. } => {
|
||||||
let op = v.to_op(self.ecx())?;
|
let op = v.to_op_for_read(self.ecx())?;
|
||||||
let idx = self.read_discriminant(&op)?;
|
let idx = self.read_discriminant(&op)?;
|
||||||
let inner = v.project_downcast(self.ecx(), idx)?;
|
let inner = v.project_downcast(self.ecx(), idx)?;
|
||||||
trace!("walk_value: variant layout: {:#?}", inner.layout());
|
trace!("walk_value: variant layout: {:#?}", inner.layout());
|
||||||
|
@ -325,5 +527,5 @@ macro_rules! make_value_visitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
make_value_visitor!(ValueVisitor,);
|
make_value_visitor!(ValueVisitor, Value,);
|
||||||
make_value_visitor!(MutValueVisitor, mut);
|
make_value_visitor!(MutValueVisitor, ValueMut, mut);
|
||||||
|
|
|
@ -652,6 +652,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||||
|
|
||||||
ProjectionElem::ConstantIndex { .. }
|
ProjectionElem::ConstantIndex { .. }
|
||||||
| ProjectionElem::Downcast(..)
|
| ProjectionElem::Downcast(..)
|
||||||
|
| ProjectionElem::OpaqueCast(..)
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
| ProjectionElem::Field(..)
|
| ProjectionElem::Field(..)
|
||||||
| ProjectionElem::Index(_) => {}
|
| ProjectionElem::Index(_) => {}
|
||||||
|
|
|
@ -316,6 +316,7 @@ where
|
||||||
|
|
||||||
ProjectionElem::Deref
|
ProjectionElem::Deref
|
||||||
| ProjectionElem::Field(_, _)
|
| ProjectionElem::Field(_, _)
|
||||||
|
| ProjectionElem::OpaqueCast(_)
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
| ProjectionElem::Downcast(_, _)
|
| ProjectionElem::Downcast(_, _)
|
||||||
|
|
|
@ -361,7 +361,7 @@ impl<'tcx> Validator<'_, 'tcx> {
|
||||||
return Err(Unpromotable);
|
return Err(Unpromotable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ProjectionElem::Downcast(..) => {
|
ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => {
|
||||||
return Err(Unpromotable);
|
return Err(Unpromotable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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
|
privacy-in-public-interface = {$vis_descr} {$kind} `{$descr}` in public interface
|
||||||
.label = can't leak {$vis_descr} {$kind}
|
.label = can't leak {$vis_descr} {$kind}
|
||||||
.visibility-label = `{$descr}` declared as {$vis_descr}
|
.visibility-label = `{$descr}` declared as {$vis_descr}
|
||||||
|
|
||||||
|
privacy-from-private-dep-in-public-interface =
|
||||||
|
{$kind} `{$descr}` from private dependency '{$krate}' in public interface
|
||||||
|
|
||||||
|
private-in-public-lint =
|
||||||
|
{$vis_descr} {$kind} `{$descr}` in public interface (error {$kind ->
|
||||||
|
[trait] E0445
|
||||||
|
*[other] E0446
|
||||||
|
})
|
||||||
|
|
|
@ -37,6 +37,7 @@ fluent_messages! {
|
||||||
expand => "../locales/en-US/expand.ftl",
|
expand => "../locales/en-US/expand.ftl",
|
||||||
lint => "../locales/en-US/lint.ftl",
|
lint => "../locales/en-US/lint.ftl",
|
||||||
parser => "../locales/en-US/parser.ftl",
|
parser => "../locales/en-US/parser.ftl",
|
||||||
|
passes => "../locales/en-US/passes.ftl",
|
||||||
privacy => "../locales/en-US/privacy.ftl",
|
privacy => "../locales/en-US/privacy.ftl",
|
||||||
typeck => "../locales/en-US/typeck.ftl",
|
typeck => "../locales/en-US/typeck.ftl",
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,35 @@ pub trait IntoDiagnosticArg {
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>;
|
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! into_diagnostic_arg_using_display {
|
||||||
|
($( $ty:ty ),+ $(,)?) => {
|
||||||
|
$(
|
||||||
|
impl IntoDiagnosticArg for $ty {
|
||||||
|
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||||
|
self.to_string().into_diagnostic_arg()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
into_diagnostic_arg_using_display!(
|
||||||
|
i8,
|
||||||
|
u8,
|
||||||
|
i16,
|
||||||
|
u16,
|
||||||
|
i32,
|
||||||
|
u32,
|
||||||
|
i64,
|
||||||
|
u64,
|
||||||
|
i128,
|
||||||
|
u128,
|
||||||
|
std::num::NonZeroU32,
|
||||||
|
hir::Target,
|
||||||
|
Edition,
|
||||||
|
Ident,
|
||||||
|
);
|
||||||
|
|
||||||
impl IntoDiagnosticArg for bool {
|
impl IntoDiagnosticArg for bool {
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||||
if self {
|
if self {
|
||||||
|
@ -50,81 +79,9 @@ impl IntoDiagnosticArg for bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoDiagnosticArg for i8 {
|
impl IntoDiagnosticArg for char {
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self)))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoDiagnosticArg for u8 {
|
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
|
||||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoDiagnosticArg for i16 {
|
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
|
||||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoDiagnosticArg for u16 {
|
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
|
||||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoDiagnosticArg for i32 {
|
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
|
||||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoDiagnosticArg for u32 {
|
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
|
||||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoDiagnosticArg for i64 {
|
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
|
||||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoDiagnosticArg for u64 {
|
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
|
||||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoDiagnosticArg for i128 {
|
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
|
||||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoDiagnosticArg for u128 {
|
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
|
||||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoDiagnosticArg for String {
|
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
|
||||||
DiagnosticArgValue::Str(Cow::Owned(self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoDiagnosticArg for std::num::NonZeroU32 {
|
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
|
||||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoDiagnosticArg for Edition {
|
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
|
||||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,15 +91,15 @@ impl IntoDiagnosticArg for Symbol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoDiagnosticArg for Ident {
|
impl<'a> IntoDiagnosticArg for &'a str {
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||||
self.to_string().into_diagnostic_arg()
|
self.to_string().into_diagnostic_arg()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoDiagnosticArg for &'a str {
|
impl IntoDiagnosticArg for String {
|
||||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||||
self.to_string().into_diagnostic_arg()
|
DiagnosticArgValue::Str(Cow::Owned(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,7 +453,7 @@ impl Diagnostic {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
|
pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self {
|
||||||
self.highlighted_note(vec![
|
self.highlighted_note(vec![
|
||||||
(format!("`{}` from trait: `", name), Style::NoStyle),
|
(format!("`{}` from trait: `", name), Style::NoStyle),
|
||||||
(signature, Style::Highlight),
|
(signature, Style::Highlight),
|
||||||
|
|
|
@ -595,6 +595,7 @@ macro_rules! error_code {
|
||||||
pub struct LintDiagnosticBuilder<'a, G: EmissionGuarantee>(DiagnosticBuilder<'a, G>);
|
pub struct LintDiagnosticBuilder<'a, G: EmissionGuarantee>(DiagnosticBuilder<'a, G>);
|
||||||
|
|
||||||
impl<'a, G: EmissionGuarantee> LintDiagnosticBuilder<'a, G> {
|
impl<'a, G: EmissionGuarantee> LintDiagnosticBuilder<'a, G> {
|
||||||
|
#[rustc_lint_diagnostics]
|
||||||
/// Return the inner `DiagnosticBuilder`, first setting the primary message to `msg`.
|
/// Return the inner `DiagnosticBuilder`, first setting the primary message to `msg`.
|
||||||
pub fn build(mut self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'a, G> {
|
pub fn build(mut self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'a, G> {
|
||||||
self.0.set_primary_message(msg);
|
self.0.set_primary_message(msg);
|
||||||
|
|
|
@ -218,10 +218,9 @@ pub fn default_submod_path<'a>(
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
|
|
||||||
let mod_name = ident.name.to_string();
|
let default_path_str = format!("{}{}.rs", relative_prefix, ident.name);
|
||||||
let default_path_str = format!("{}{}.rs", relative_prefix, mod_name);
|
|
||||||
let secondary_path_str =
|
let secondary_path_str =
|
||||||
format!("{}{}{}mod.rs", relative_prefix, mod_name, path::MAIN_SEPARATOR);
|
format!("{}{}{}mod.rs", relative_prefix, ident.name, path::MAIN_SEPARATOR);
|
||||||
let default_path = dir_path.join(&default_path_str);
|
let default_path = dir_path.join(&default_path_str);
|
||||||
let secondary_path = dir_path.join(&secondary_path_str);
|
let secondary_path = dir_path.join(&secondary_path_str);
|
||||||
let default_exists = sess.source_map().file_exists(&default_path);
|
let default_exists = sess.source_map().file_exists(&default_path);
|
||||||
|
|
|
@ -138,7 +138,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPr
|
||||||
if let TypeVariableOriginKind::TypeParameterDefinition(name, _) =
|
if let TypeVariableOriginKind::TypeParameterDefinition(name, _) =
|
||||||
infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind
|
infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind
|
||||||
{
|
{
|
||||||
Some(name.to_string())
|
Some(name)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPr
|
||||||
if let ConstVariableOriginKind::ConstParameterDefinition(name, _) =
|
if let ConstVariableOriginKind::ConstParameterDefinition(name, _) =
|
||||||
infcx.inner.borrow_mut().const_unification_table().probe_value(ct_vid).origin.kind
|
infcx.inner.borrow_mut().const_unification_table().probe_value(ct_vid).origin.kind
|
||||||
{
|
{
|
||||||
return Some(name.to_string());
|
return Some(name);
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
|
||||||
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
||||||
}
|
}
|
||||||
(Some(DiagnosticDeriveKind::Lint), _) => {
|
(Some(DiagnosticDeriveKind::Lint), _) => {
|
||||||
span_err(span, "only `#[error(..)]` and `#[warn(..)]` are supported")
|
span_err(span, "only `#[error(..)]` and `#[warning(..)]` are supported")
|
||||||
.help("use the `#[error(...)]` attribute to create a error")
|
.help("use the `#[error(...)]` attribute to create a error")
|
||||||
.emit();
|
.emit();
|
||||||
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
||||||
|
|
|
@ -8,12 +8,13 @@ use crate::diagnostics::utils::{
|
||||||
report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
|
report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
|
||||||
Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
|
Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
|
||||||
};
|
};
|
||||||
use proc_macro2::{Ident, TokenStream};
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_quote, spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type,
|
parse_quote, spanned::Spanned, Attribute, Field, Meta, MetaList, MetaNameValue, NestedMeta,
|
||||||
|
Path, Type,
|
||||||
};
|
};
|
||||||
use synstructure::{BindingInfo, Structure};
|
use synstructure::{BindingInfo, Structure};
|
||||||
|
|
||||||
|
@ -80,8 +81,8 @@ impl DiagnosticDeriveBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn body<'s>(&mut self, structure: &mut Structure<'s>) -> (TokenStream, TokenStream) {
|
pub fn body<'s>(&mut self, structure: &mut Structure<'s>) -> (TokenStream, TokenStream) {
|
||||||
// Keep track of which fields are subdiagnostics or have no attributes.
|
// Keep track of which fields need to be handled with a by-move binding.
|
||||||
let mut subdiagnostics_or_empty = std::collections::HashSet::new();
|
let mut needs_moved = std::collections::HashSet::new();
|
||||||
|
|
||||||
// Generates calls to `span_label` and similar functions based on the attributes
|
// Generates calls to `span_label` and similar functions based on the attributes
|
||||||
// on fields. Code for suggestions uses formatting machinery and the value of
|
// on fields. Code for suggestions uses formatting machinery and the value of
|
||||||
|
@ -92,16 +93,11 @@ impl DiagnosticDeriveBuilder {
|
||||||
let attrs = structure
|
let attrs = structure
|
||||||
.clone()
|
.clone()
|
||||||
.filter(|field_binding| {
|
.filter(|field_binding| {
|
||||||
let attrs = &field_binding.ast().attrs;
|
let ast = &field_binding.ast();
|
||||||
|
!self.needs_move(ast) || {
|
||||||
(!attrs.is_empty()
|
needs_moved.insert(field_binding.binding.clone());
|
||||||
&& attrs.iter().all(|attr| {
|
false
|
||||||
"subdiagnostic" != attr.path.segments.last().unwrap().ident.to_string()
|
}
|
||||||
}))
|
|
||||||
|| {
|
|
||||||
subdiagnostics_or_empty.insert(field_binding.binding.clone());
|
|
||||||
false
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.each(|field_binding| self.generate_field_attrs_code(field_binding));
|
.each(|field_binding| self.generate_field_attrs_code(field_binding));
|
||||||
|
|
||||||
|
@ -111,12 +107,41 @@ impl DiagnosticDeriveBuilder {
|
||||||
// attributes or a `#[subdiagnostic]` attribute then it must be passed as an
|
// attributes or a `#[subdiagnostic]` attribute then it must be passed as an
|
||||||
// argument to the diagnostic so that it can be referred to by Fluent messages.
|
// argument to the diagnostic so that it can be referred to by Fluent messages.
|
||||||
let args = structure
|
let args = structure
|
||||||
.filter(|field_binding| subdiagnostics_or_empty.contains(&field_binding.binding))
|
.filter(|field_binding| needs_moved.contains(&field_binding.binding))
|
||||||
.each(|field_binding| self.generate_field_attrs_code(field_binding));
|
.each(|field_binding| self.generate_field_attrs_code(field_binding));
|
||||||
|
|
||||||
(attrs, args)
|
(attrs, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic
|
||||||
|
/// call (like `span_label`).
|
||||||
|
fn should_generate_set_arg(&self, field: &Field) -> bool {
|
||||||
|
field.attrs.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if `field` needs to have code generated in the by-move branch of the
|
||||||
|
/// generated derive rather than the by-ref branch.
|
||||||
|
fn needs_move(&self, field: &Field) -> bool {
|
||||||
|
let generates_set_arg = self.should_generate_set_arg(field);
|
||||||
|
let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]);
|
||||||
|
// FIXME(davidtwco): better support for one field needing to be in the by-move and
|
||||||
|
// by-ref branches.
|
||||||
|
let is_subdiagnostic = field
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.map(|attr| attr.path.segments.last().unwrap().ident.to_string())
|
||||||
|
.any(|attr| attr == "subdiagnostic");
|
||||||
|
|
||||||
|
// `set_arg` calls take their argument by-move..
|
||||||
|
generates_set_arg
|
||||||
|
// If this is a `MultiSpan` field then it needs to be moved to be used by any
|
||||||
|
// attribute..
|
||||||
|
|| is_multispan
|
||||||
|
// If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is
|
||||||
|
// unlikely to be `Copy`..
|
||||||
|
|| is_subdiagnostic
|
||||||
|
}
|
||||||
|
|
||||||
/// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
|
/// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
|
||||||
/// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates
|
/// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates
|
||||||
/// diagnostic builder calls for setting error code and creating note/help messages.
|
/// diagnostic builder calls for setting error code and creating note/help messages.
|
||||||
|
@ -131,7 +156,7 @@ impl DiagnosticDeriveBuilder {
|
||||||
let name = name.as_str();
|
let name = name.as_str();
|
||||||
let meta = attr.parse_meta()?;
|
let meta = attr.parse_meta()?;
|
||||||
|
|
||||||
let is_help_or_note = matches!(name, "help" | "note");
|
let is_help_note_or_warn = matches!(name, "help" | "note" | "warn_");
|
||||||
|
|
||||||
let nested = match meta {
|
let nested = match meta {
|
||||||
// Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or
|
// Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or
|
||||||
|
@ -139,8 +164,12 @@ impl DiagnosticDeriveBuilder {
|
||||||
Meta::List(MetaList { ref nested, .. }) => nested,
|
Meta::List(MetaList { ref nested, .. }) => nested,
|
||||||
// Subdiagnostics without spans can be applied to the type too, and these are just
|
// Subdiagnostics without spans can be applied to the type too, and these are just
|
||||||
// paths: `#[help]` and `#[note]`
|
// paths: `#[help]` and `#[note]`
|
||||||
Meta::Path(_) if is_help_or_note => {
|
Meta::Path(_) if is_help_note_or_warn => {
|
||||||
let fn_name = proc_macro2::Ident::new(name, attr.span());
|
let fn_name = if name == "warn_" {
|
||||||
|
Ident::new("warn", attr.span())
|
||||||
|
} else {
|
||||||
|
Ident::new(name, attr.span())
|
||||||
|
};
|
||||||
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); });
|
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); });
|
||||||
}
|
}
|
||||||
_ => throw_invalid_attr!(attr, &meta),
|
_ => throw_invalid_attr!(attr, &meta),
|
||||||
|
@ -152,9 +181,11 @@ impl DiagnosticDeriveBuilder {
|
||||||
"error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)),
|
"error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)),
|
||||||
"warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)),
|
"warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)),
|
||||||
"lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)),
|
"lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)),
|
||||||
"help" | "note" => (),
|
"help" | "note" | "warn_" => (),
|
||||||
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
||||||
diag.help("only `error`, `warning`, `help` and `note` are valid attributes")
|
diag.help(
|
||||||
|
"only `error`, `warning`, `help`, `note` and `warn_` are valid attributes",
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,14 +194,16 @@ impl DiagnosticDeriveBuilder {
|
||||||
let mut nested_iter = nested.into_iter();
|
let mut nested_iter = nested.into_iter();
|
||||||
if let Some(nested_attr) = nested_iter.next() {
|
if let Some(nested_attr) = nested_iter.next() {
|
||||||
// Report an error if there are any other list items after the path.
|
// Report an error if there are any other list items after the path.
|
||||||
if is_help_or_note && nested_iter.next().is_some() {
|
if is_help_note_or_warn && nested_iter.next().is_some() {
|
||||||
throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||||
diag.help("`help` and `note` struct attributes can only have one argument")
|
diag.help(
|
||||||
|
"`help`, `note` and `warn_` struct attributes can only have one argument",
|
||||||
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
match nested_attr {
|
match nested_attr {
|
||||||
NestedMeta::Meta(Meta::Path(path)) if is_help_or_note => {
|
NestedMeta::Meta(Meta::Path(path)) if is_help_note_or_warn => {
|
||||||
let fn_name = proc_macro2::Ident::new(name, attr.span());
|
let fn_name = proc_macro2::Ident::new(name, attr.span());
|
||||||
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
|
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
|
||||||
}
|
}
|
||||||
|
@ -178,7 +211,7 @@ impl DiagnosticDeriveBuilder {
|
||||||
self.slug.set_once((path.clone(), span));
|
self.slug.set_once((path.clone(), span));
|
||||||
}
|
}
|
||||||
NestedMeta::Meta(meta @ Meta::NameValue(_))
|
NestedMeta::Meta(meta @ Meta::NameValue(_))
|
||||||
if !is_help_or_note
|
if !is_help_note_or_warn
|
||||||
&& meta.path().segments.last().unwrap().ident.to_string() == "code" =>
|
&& meta.path().segments.last().unwrap().ident.to_string() == "code" =>
|
||||||
{
|
{
|
||||||
// don't error for valid follow-up attributes
|
// don't error for valid follow-up attributes
|
||||||
|
@ -227,57 +260,55 @@ impl DiagnosticDeriveBuilder {
|
||||||
let field = binding_info.ast();
|
let field = binding_info.ast();
|
||||||
let field_binding = &binding_info.binding;
|
let field_binding = &binding_info.binding;
|
||||||
|
|
||||||
let inner_ty = FieldInnerTy::from_type(&field.ty);
|
if self.should_generate_set_arg(&field) {
|
||||||
|
|
||||||
// When generating `set_arg` or `add_subdiagnostic` calls, move data rather than
|
|
||||||
// borrow it to avoid requiring clones - this must therefore be the last use of
|
|
||||||
// each field (for example, any formatting machinery that might refer to a field
|
|
||||||
// should be generated already).
|
|
||||||
if field.attrs.is_empty() {
|
|
||||||
let diag = &self.diag;
|
let diag = &self.diag;
|
||||||
let ident = field.ident.as_ref().unwrap();
|
let ident = field.ident.as_ref().unwrap();
|
||||||
quote! {
|
return quote! {
|
||||||
#diag.set_arg(
|
#diag.set_arg(
|
||||||
stringify!(#ident),
|
stringify!(#ident),
|
||||||
#field_binding
|
#field_binding
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
} else {
|
|
||||||
field
|
|
||||||
.attrs
|
|
||||||
.iter()
|
|
||||||
.map(move |attr| {
|
|
||||||
let name = attr.path.segments.last().unwrap().ident.to_string();
|
|
||||||
let (binding, needs_destructure) = match (name.as_str(), &inner_ty) {
|
|
||||||
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
|
|
||||||
("primary_span", FieldInnerTy::Vec(_)) => {
|
|
||||||
(quote! { #field_binding.clone() }, false)
|
|
||||||
}
|
|
||||||
// `subdiagnostics` are not derefed because they are bound by value.
|
|
||||||
("subdiagnostic", _) => (quote! { #field_binding }, true),
|
|
||||||
_ => (quote! { *#field_binding }, true),
|
|
||||||
};
|
|
||||||
|
|
||||||
let generated_code = self
|
|
||||||
.generate_inner_field_code(
|
|
||||||
attr,
|
|
||||||
FieldInfo {
|
|
||||||
binding: binding_info,
|
|
||||||
ty: inner_ty.inner_type().unwrap_or(&field.ty),
|
|
||||||
span: &field.span(),
|
|
||||||
},
|
|
||||||
binding,
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|v| v.to_compile_error());
|
|
||||||
|
|
||||||
if needs_destructure {
|
|
||||||
inner_ty.with(field_binding, generated_code)
|
|
||||||
} else {
|
|
||||||
generated_code
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let needs_move = self.needs_move(&field);
|
||||||
|
let inner_ty = FieldInnerTy::from_type(&field.ty);
|
||||||
|
|
||||||
|
field
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.map(move |attr| {
|
||||||
|
let name = attr.path.segments.last().unwrap().ident.to_string();
|
||||||
|
let needs_clone =
|
||||||
|
name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_));
|
||||||
|
let (binding, needs_destructure) = if needs_clone {
|
||||||
|
// `primary_span` can accept a `Vec<Span>` so don't destructure that.
|
||||||
|
(quote! { #field_binding.clone() }, false)
|
||||||
|
} else if needs_move {
|
||||||
|
(quote! { #field_binding }, true)
|
||||||
|
} else {
|
||||||
|
(quote! { *#field_binding }, true)
|
||||||
|
};
|
||||||
|
|
||||||
|
let generated_code = self
|
||||||
|
.generate_inner_field_code(
|
||||||
|
attr,
|
||||||
|
FieldInfo {
|
||||||
|
binding: binding_info,
|
||||||
|
ty: inner_ty.inner_type().unwrap_or(&field.ty),
|
||||||
|
span: &field.span(),
|
||||||
|
},
|
||||||
|
binding,
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|v| v.to_compile_error());
|
||||||
|
|
||||||
|
if needs_destructure {
|
||||||
|
inner_ty.with(field_binding, generated_code)
|
||||||
|
} else {
|
||||||
|
generated_code
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_inner_field_code(
|
fn generate_inner_field_code(
|
||||||
|
@ -324,10 +355,12 @@ impl DiagnosticDeriveBuilder {
|
||||||
report_error_if_not_applied_to_span(attr, &info)?;
|
report_error_if_not_applied_to_span(attr, &info)?;
|
||||||
Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label }))
|
Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label }))
|
||||||
}
|
}
|
||||||
"note" | "help" => {
|
"note" | "help" | "warn_" => {
|
||||||
let path = match name {
|
let warn_ident = Ident::new("warn", Span::call_site());
|
||||||
"note" => parse_quote! { _subdiag::note },
|
let (ident, path) = match name {
|
||||||
"help" => parse_quote! { _subdiag::help },
|
"note" => (ident, parse_quote! { _subdiag::note }),
|
||||||
|
"help" => (ident, parse_quote! { _subdiag::help }),
|
||||||
|
"warn_" => (&warn_ident, parse_quote! { _subdiag::warn }),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
|
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
|
||||||
|
@ -364,10 +397,10 @@ impl DiagnosticDeriveBuilder {
|
||||||
"suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => {
|
"suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => {
|
||||||
return self.generate_inner_field_code_suggestion(attr, info);
|
return self.generate_inner_field_code_suggestion(attr, info);
|
||||||
}
|
}
|
||||||
"label" | "help" | "note" => (),
|
"label" | "help" | "note" | "warn_" => (),
|
||||||
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
||||||
diag.help(
|
diag.help(
|
||||||
"only `label`, `note`, `help` or `suggestion{,_short,_hidden,_verbose}` are \
|
"only `label`, `help`, `note`, `warn` or `suggestion{,_short,_hidden,_verbose}` are \
|
||||||
valid field attributes",
|
valid field attributes",
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
@ -396,7 +429,14 @@ impl DiagnosticDeriveBuilder {
|
||||||
Ok(self.add_spanned_subdiagnostic(binding, ident, msg))
|
Ok(self.add_spanned_subdiagnostic(binding, ident, msg))
|
||||||
}
|
}
|
||||||
"note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)),
|
"note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)),
|
||||||
"note" | "help" => report_type_error(attr, "`Span` or `()`")?,
|
// `warn_` must be special-cased because the attribute `warn` already has meaning and
|
||||||
|
// so isn't used, despite the diagnostic API being named `warn`.
|
||||||
|
"warn_" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => Ok(self
|
||||||
|
.add_spanned_subdiagnostic(binding, &Ident::new("warn", Span::call_site()), msg)),
|
||||||
|
"warn_" if type_is_unit(&info.ty) => {
|
||||||
|
Ok(self.add_subdiagnostic(&Ident::new("warn", Span::call_site()), msg))
|
||||||
|
}
|
||||||
|
"note" | "help" | "warn_" => report_type_error(attr, "`Span` or `()`")?,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,10 +260,12 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
|
||||||
#generated
|
#generated
|
||||||
|
|
||||||
pub mod _subdiag {
|
pub mod _subdiag {
|
||||||
pub const note: crate::SubdiagnosticMessage =
|
|
||||||
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
|
|
||||||
pub const help: crate::SubdiagnosticMessage =
|
pub const help: crate::SubdiagnosticMessage =
|
||||||
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help"));
|
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help"));
|
||||||
|
pub const note: crate::SubdiagnosticMessage =
|
||||||
|
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
|
||||||
|
pub const warn: crate::SubdiagnosticMessage =
|
||||||
|
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("warn"));
|
||||||
pub const label: crate::SubdiagnosticMessage =
|
pub const label: crate::SubdiagnosticMessage =
|
||||||
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label"));
|
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label"));
|
||||||
pub const suggestion: crate::SubdiagnosticMessage =
|
pub const suggestion: crate::SubdiagnosticMessage =
|
||||||
|
|
|
@ -37,6 +37,8 @@ enum SubdiagnosticKind {
|
||||||
Note,
|
Note,
|
||||||
/// `#[help(...)]`
|
/// `#[help(...)]`
|
||||||
Help,
|
Help,
|
||||||
|
/// `#[warn_(...)]`
|
||||||
|
Warn,
|
||||||
/// `#[suggestion{,_short,_hidden,_verbose}]`
|
/// `#[suggestion{,_short,_hidden,_verbose}]`
|
||||||
Suggestion(SubdiagnosticSuggestionKind),
|
Suggestion(SubdiagnosticSuggestionKind),
|
||||||
}
|
}
|
||||||
|
@ -49,6 +51,7 @@ impl FromStr for SubdiagnosticKind {
|
||||||
"label" => Ok(SubdiagnosticKind::Label),
|
"label" => Ok(SubdiagnosticKind::Label),
|
||||||
"note" => Ok(SubdiagnosticKind::Note),
|
"note" => Ok(SubdiagnosticKind::Note),
|
||||||
"help" => Ok(SubdiagnosticKind::Help),
|
"help" => Ok(SubdiagnosticKind::Help),
|
||||||
|
"warn_" => Ok(SubdiagnosticKind::Warn),
|
||||||
"suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
|
"suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
|
||||||
"suggestion_short" => {
|
"suggestion_short" => {
|
||||||
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
|
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
|
||||||
|
@ -70,6 +73,7 @@ impl quote::IdentFragment for SubdiagnosticKind {
|
||||||
SubdiagnosticKind::Label => write!(f, "label"),
|
SubdiagnosticKind::Label => write!(f, "label"),
|
||||||
SubdiagnosticKind::Note => write!(f, "note"),
|
SubdiagnosticKind::Note => write!(f, "note"),
|
||||||
SubdiagnosticKind::Help => write!(f, "help"),
|
SubdiagnosticKind::Help => write!(f, "help"),
|
||||||
|
SubdiagnosticKind::Warn => write!(f, "warn"),
|
||||||
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
|
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
|
||||||
write!(f, "suggestion")
|
write!(f, "suggestion")
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,13 @@ pub(crate) fn report_error_if_not_applied_to_span(
|
||||||
attr: &Attribute,
|
attr: &Attribute,
|
||||||
info: &FieldInfo<'_>,
|
info: &FieldInfo<'_>,
|
||||||
) -> Result<(), DiagnosticDeriveError> {
|
) -> Result<(), DiagnosticDeriveError> {
|
||||||
report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`")
|
if !type_matches_path(&info.ty, &["rustc_span", "Span"])
|
||||||
|
&& !type_matches_path(&info.ty, &["rustc_errors", "MultiSpan"])
|
||||||
|
{
|
||||||
|
report_type_error(attr, "`Span` or `MultiSpan`")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inner type of a field and type of wrapper.
|
/// Inner type of a field and type of wrapper.
|
||||||
|
|
|
@ -130,8 +130,9 @@ decl_derive!(
|
||||||
warning,
|
warning,
|
||||||
error,
|
error,
|
||||||
lint,
|
lint,
|
||||||
note,
|
|
||||||
help,
|
help,
|
||||||
|
note,
|
||||||
|
warn_,
|
||||||
// field attributes
|
// field attributes
|
||||||
skip_arg,
|
skip_arg,
|
||||||
primary_span,
|
primary_span,
|
||||||
|
@ -148,8 +149,9 @@ decl_derive!(
|
||||||
warning,
|
warning,
|
||||||
error,
|
error,
|
||||||
lint,
|
lint,
|
||||||
note,
|
|
||||||
help,
|
help,
|
||||||
|
note,
|
||||||
|
warn_,
|
||||||
// field attributes
|
// field attributes
|
||||||
skip_arg,
|
skip_arg,
|
||||||
primary_span,
|
primary_span,
|
||||||
|
@ -166,6 +168,7 @@ decl_derive!(
|
||||||
label,
|
label,
|
||||||
help,
|
help,
|
||||||
note,
|
note,
|
||||||
|
warn_,
|
||||||
suggestion,
|
suggestion,
|
||||||
suggestion_short,
|
suggestion_short,
|
||||||
suggestion_hidden,
|
suggestion_hidden,
|
||||||
|
|
|
@ -1397,6 +1397,7 @@ impl<V, T> ProjectionElem<V, T> {
|
||||||
|
|
||||||
Self::Field(_, _)
|
Self::Field(_, _)
|
||||||
| Self::Index(_)
|
| Self::Index(_)
|
||||||
|
| Self::OpaqueCast(_)
|
||||||
| Self::ConstantIndex { .. }
|
| Self::ConstantIndex { .. }
|
||||||
| Self::Subslice { .. }
|
| Self::Subslice { .. }
|
||||||
| Self::Downcast(_, _) => false,
|
| Self::Downcast(_, _) => false,
|
||||||
|
@ -1574,7 +1575,9 @@ impl Debug for Place<'_> {
|
||||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||||
for elem in self.projection.iter().rev() {
|
for elem in self.projection.iter().rev() {
|
||||||
match elem {
|
match elem {
|
||||||
ProjectionElem::Downcast(_, _) | ProjectionElem::Field(_, _) => {
|
ProjectionElem::OpaqueCast(_)
|
||||||
|
| ProjectionElem::Downcast(_, _)
|
||||||
|
| ProjectionElem::Field(_, _) => {
|
||||||
write!(fmt, "(").unwrap();
|
write!(fmt, "(").unwrap();
|
||||||
}
|
}
|
||||||
ProjectionElem::Deref => {
|
ProjectionElem::Deref => {
|
||||||
|
@ -1590,6 +1593,9 @@ impl Debug for Place<'_> {
|
||||||
|
|
||||||
for elem in self.projection.iter() {
|
for elem in self.projection.iter() {
|
||||||
match elem {
|
match elem {
|
||||||
|
ProjectionElem::OpaqueCast(ty) => {
|
||||||
|
write!(fmt, " as {})", ty)?;
|
||||||
|
}
|
||||||
ProjectionElem::Downcast(Some(name), _index) => {
|
ProjectionElem::Downcast(Some(name), _index) => {
|
||||||
write!(fmt, " as {})", name)?;
|
write!(fmt, " as {})", name)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -754,6 +754,9 @@ pub type AssertMessage<'tcx> = AssertKind<Operand<'tcx>>;
|
||||||
/// generator has more than one variant, the parent place's variant index must be set, indicating
|
/// generator has more than one variant, the parent place's variant index must be set, indicating
|
||||||
/// which variant is being used. If it has just one variant, the variant index may or may not be
|
/// which variant is being used. If it has just one variant, the variant index may or may not be
|
||||||
/// included - the single possible variant is inferred if it is not included.
|
/// included - the single possible variant is inferred if it is not included.
|
||||||
|
/// - [`OpaqueCast`](ProjectionElem::OpaqueCast): This projection changes the place's type to the
|
||||||
|
/// given one, and makes no other changes. A `OpaqueCast` projection on any type other than an
|
||||||
|
/// opaque type from the current crate is not well-formed.
|
||||||
/// - [`ConstantIndex`](ProjectionElem::ConstantIndex): Computes an offset in units of `T` into the
|
/// - [`ConstantIndex`](ProjectionElem::ConstantIndex): Computes an offset in units of `T` into the
|
||||||
/// place as described in the documentation for the `ProjectionElem`. The resulting address is
|
/// place as described in the documentation for the `ProjectionElem`. The resulting address is
|
||||||
/// the parent's address plus that offset, and the type is `T`. This is only legal if the parent
|
/// the parent's address plus that offset, and the type is `T`. This is only legal if the parent
|
||||||
|
@ -856,6 +859,10 @@ pub enum ProjectionElem<V, T> {
|
||||||
///
|
///
|
||||||
/// The included Symbol is the name of the variant, used for printing MIR.
|
/// The included Symbol is the name of the variant, used for printing MIR.
|
||||||
Downcast(Option<Symbol>, VariantIdx),
|
Downcast(Option<Symbol>, VariantIdx),
|
||||||
|
|
||||||
|
/// Like an explicit cast from an opaque type to a concrete type, but without
|
||||||
|
/// requiring an intermediate variable.
|
||||||
|
OpaqueCast(T),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Alias for projections as they appear in places, where the base is a place
|
/// Alias for projections as they appear in places, where the base is a place
|
||||||
|
|
|
@ -57,7 +57,7 @@ impl<'tcx> PlaceTy<'tcx> {
|
||||||
/// `PlaceElem`, where we can just use the `Ty` that is already
|
/// `PlaceElem`, where we can just use the `Ty` that is already
|
||||||
/// stored inline on field projection elems.
|
/// stored inline on field projection elems.
|
||||||
pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> {
|
pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> {
|
||||||
self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty)
|
self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty, |_, ty| ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
|
/// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
|
||||||
|
@ -71,6 +71,7 @@ impl<'tcx> PlaceTy<'tcx> {
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
elem: &ProjectionElem<V, T>,
|
elem: &ProjectionElem<V, T>,
|
||||||
mut handle_field: impl FnMut(&Self, Field, T) -> Ty<'tcx>,
|
mut handle_field: impl FnMut(&Self, Field, T) -> Ty<'tcx>,
|
||||||
|
mut handle_opaque_cast: impl FnMut(&Self, T) -> Ty<'tcx>,
|
||||||
) -> PlaceTy<'tcx>
|
) -> PlaceTy<'tcx>
|
||||||
where
|
where
|
||||||
V: ::std::fmt::Debug,
|
V: ::std::fmt::Debug,
|
||||||
|
@ -109,6 +110,7 @@ impl<'tcx> PlaceTy<'tcx> {
|
||||||
PlaceTy { ty: self.ty, variant_index: Some(index) }
|
PlaceTy { ty: self.ty, variant_index: Some(index) }
|
||||||
}
|
}
|
||||||
ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
|
ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
|
||||||
|
ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast(&self, ty)),
|
||||||
};
|
};
|
||||||
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
|
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
|
||||||
answer
|
answer
|
||||||
|
|
|
@ -182,6 +182,7 @@ impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Deref => Deref,
|
Deref => Deref,
|
||||||
Field(f, ty) => Field(f, ty.try_fold_with(folder)?),
|
Field(f, ty) => Field(f, ty.try_fold_with(folder)?),
|
||||||
|
OpaqueCast(ty) => OpaqueCast(ty.try_fold_with(folder)?),
|
||||||
Index(v) => Index(v.try_fold_with(folder)?),
|
Index(v) => Index(v.try_fold_with(folder)?),
|
||||||
Downcast(symbol, variantidx) => Downcast(symbol, variantidx),
|
Downcast(symbol, variantidx) => Downcast(symbol, variantidx),
|
||||||
ConstantIndex { offset, min_length, from_end } => {
|
ConstantIndex { offset, min_length, from_end } => {
|
||||||
|
|
|
@ -1064,6 +1064,11 @@ macro_rules! visit_place_fns {
|
||||||
self.visit_ty(&mut new_ty, TyContext::Location(location));
|
self.visit_ty(&mut new_ty, TyContext::Location(location));
|
||||||
if ty != new_ty { Some(PlaceElem::Field(field, new_ty)) } else { None }
|
if ty != new_ty { Some(PlaceElem::Field(field, new_ty)) } else { None }
|
||||||
}
|
}
|
||||||
|
PlaceElem::OpaqueCast(ty) => {
|
||||||
|
let mut new_ty = ty;
|
||||||
|
self.visit_ty(&mut new_ty, TyContext::Location(location));
|
||||||
|
if ty != new_ty { Some(PlaceElem::OpaqueCast(new_ty)) } else { None }
|
||||||
|
}
|
||||||
PlaceElem::Deref
|
PlaceElem::Deref
|
||||||
| PlaceElem::ConstantIndex { .. }
|
| PlaceElem::ConstantIndex { .. }
|
||||||
| PlaceElem::Subslice { .. }
|
| PlaceElem::Subslice { .. }
|
||||||
|
@ -1133,7 +1138,7 @@ macro_rules! visit_place_fns {
|
||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) {
|
||||||
match elem {
|
match elem {
|
||||||
ProjectionElem::Field(_field, ty) => {
|
ProjectionElem::OpaqueCast(ty) | ProjectionElem::Field(_, ty) => {
|
||||||
self.visit_ty(ty, TyContext::Location(location));
|
self.visit_ty(ty, TyContext::Location(location));
|
||||||
}
|
}
|
||||||
ProjectionElem::Index(local) => {
|
ProjectionElem::Index(local) => {
|
||||||
|
|
|
@ -542,17 +542,9 @@ pub enum SelectionError<'tcx> {
|
||||||
ErrorReporting,
|
ErrorReporting,
|
||||||
/// Multiple applicable `impl`s where found. The `DefId`s correspond to
|
/// Multiple applicable `impl`s where found. The `DefId`s correspond to
|
||||||
/// all the `impl`s' Items.
|
/// all the `impl`s' Items.
|
||||||
Ambiguous(Vec<AmbiguousSelection>),
|
Ambiguous(Vec<DefId>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub enum AmbiguousSelection {
|
|
||||||
Impl(DefId),
|
|
||||||
ParamEnv(Span),
|
|
||||||
}
|
|
||||||
|
|
||||||
TrivialTypeTraversalAndLiftImpls! { AmbiguousSelection, }
|
|
||||||
|
|
||||||
/// When performing resolution, it is typically the case that there
|
/// When performing resolution, it is typically the case that there
|
||||||
/// can be one of three outcomes:
|
/// can be one of three outcomes:
|
||||||
///
|
///
|
||||||
|
|
|
@ -1975,7 +1975,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||||
min_size = field_end;
|
min_size = field_end;
|
||||||
}
|
}
|
||||||
FieldInfo {
|
FieldInfo {
|
||||||
name: name.to_string(),
|
name,
|
||||||
offset: offset.bytes(),
|
offset: offset.bytes(),
|
||||||
size: field_layout.size.bytes(),
|
size: field_layout.size.bytes(),
|
||||||
align: field_layout.align.abi.bytes(),
|
align: field_layout.align.abi.bytes(),
|
||||||
|
@ -1984,7 +1984,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
VariantInfo {
|
VariantInfo {
|
||||||
name: n.map(|n| n.to_string()),
|
name: n,
|
||||||
kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact },
|
kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact },
|
||||||
align: layout.align.abi.bytes(),
|
align: layout.align.abi.bytes(),
|
||||||
size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() },
|
size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() },
|
||||||
|
|
|
@ -1030,11 +1030,11 @@ pub trait PrettyPrinter<'tcx>:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ty_infer_name(&self, _: ty::TyVid) -> Option<String> {
|
fn ty_infer_name(&self, _: ty::TyVid) -> Option<Symbol> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn const_infer_name(&self, _: ty::ConstVid<'tcx>) -> Option<String> {
|
fn const_infer_name(&self, _: ty::ConstVid<'tcx>) -> Option<Symbol> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1550,8 +1550,8 @@ pub struct FmtPrinterData<'a, 'tcx> {
|
||||||
|
|
||||||
pub region_highlight_mode: RegionHighlightMode<'tcx>,
|
pub region_highlight_mode: RegionHighlightMode<'tcx>,
|
||||||
|
|
||||||
pub ty_infer_name_resolver: Option<Box<dyn Fn(ty::TyVid) -> Option<String> + 'a>>,
|
pub ty_infer_name_resolver: Option<Box<dyn Fn(ty::TyVid) -> Option<Symbol> + 'a>>,
|
||||||
pub const_infer_name_resolver: Option<Box<dyn Fn(ty::ConstVid<'tcx>) -> Option<String> + 'a>>,
|
pub const_infer_name_resolver: Option<Box<dyn Fn(ty::ConstVid<'tcx>) -> Option<Symbol> + 'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> Deref for FmtPrinter<'a, 'tcx> {
|
impl<'a, 'tcx> Deref for FmtPrinter<'a, 'tcx> {
|
||||||
|
@ -1841,11 +1841,11 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
|
impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
|
||||||
fn ty_infer_name(&self, id: ty::TyVid) -> Option<String> {
|
fn ty_infer_name(&self, id: ty::TyVid) -> Option<Symbol> {
|
||||||
self.0.ty_infer_name_resolver.as_ref().and_then(|func| func(id))
|
self.0.ty_infer_name_resolver.as_ref().and_then(|func| func(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn const_infer_name(&self, id: ty::ConstVid<'tcx>) -> Option<String> {
|
fn const_infer_name(&self, id: ty::ConstVid<'tcx>) -> Option<Symbol> {
|
||||||
self.0.const_infer_name_resolver.as_ref().and_then(|func| func(id))
|
self.0.const_infer_name_resolver.as_ref().and_then(|func| func(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_middle::hir::place::Projection as HirProjection;
|
use rustc_middle::hir::place::Projection as HirProjection;
|
||||||
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
|
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
|
||||||
use rustc_middle::middle::region;
|
use rustc_middle::middle::region;
|
||||||
|
use rustc_middle::mir::tcx::PlaceTy;
|
||||||
use rustc_middle::mir::AssertKind::BoundsCheck;
|
use rustc_middle::mir::AssertKind::BoundsCheck;
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::thir::*;
|
use rustc_middle::thir::*;
|
||||||
|
@ -71,7 +72,7 @@ pub(crate) enum PlaceBase {
|
||||||
/// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
|
/// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
|
||||||
/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
|
/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub(crate) struct PlaceBuilder<'tcx> {
|
pub(in crate::build) struct PlaceBuilder<'tcx> {
|
||||||
base: PlaceBase,
|
base: PlaceBase,
|
||||||
projection: Vec<PlaceElem<'tcx>>,
|
projection: Vec<PlaceElem<'tcx>>,
|
||||||
}
|
}
|
||||||
|
@ -104,6 +105,8 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
|
||||||
variant = Some(*idx);
|
variant = Some(*idx);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// These do not affect anything, they just make sure we know the right type.
|
||||||
|
ProjectionElem::OpaqueCast(_) => continue,
|
||||||
ProjectionElem::Index(..)
|
ProjectionElem::Index(..)
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
| ProjectionElem::Subslice { .. } => {
|
| ProjectionElem::Subslice { .. } => {
|
||||||
|
@ -201,10 +204,10 @@ fn find_capture_matching_projections<'a, 'tcx>(
|
||||||
/// `PlaceBuilder` now starts from `PlaceBase::Local`.
|
/// `PlaceBuilder` now starts from `PlaceBase::Local`.
|
||||||
///
|
///
|
||||||
/// Returns a Result with the error being the PlaceBuilder (`from_builder`) that was not found.
|
/// Returns a Result with the error being the PlaceBuilder (`from_builder`) that was not found.
|
||||||
fn to_upvars_resolved_place_builder<'a, 'tcx>(
|
#[instrument(level = "trace", skip(cx))]
|
||||||
|
fn to_upvars_resolved_place_builder<'tcx>(
|
||||||
from_builder: PlaceBuilder<'tcx>,
|
from_builder: PlaceBuilder<'tcx>,
|
||||||
tcx: TyCtxt<'tcx>,
|
cx: &Builder<'_, 'tcx>,
|
||||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
|
||||||
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
|
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
|
||||||
match from_builder.base {
|
match from_builder.base {
|
||||||
PlaceBase::Local(_) => Ok(from_builder),
|
PlaceBase::Local(_) => Ok(from_builder),
|
||||||
|
@ -219,13 +222,13 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
|
||||||
|
|
||||||
let Some((capture_index, capture)) =
|
let Some((capture_index, capture)) =
|
||||||
find_capture_matching_projections(
|
find_capture_matching_projections(
|
||||||
typeck_results,
|
cx.typeck_results,
|
||||||
var_hir_id,
|
var_hir_id,
|
||||||
closure_def_id,
|
closure_def_id,
|
||||||
&from_builder.projection,
|
&from_builder.projection,
|
||||||
) else {
|
) else {
|
||||||
let closure_span = tcx.def_span(closure_def_id);
|
let closure_span = cx.tcx.def_span(closure_def_id);
|
||||||
if !enable_precise_capture(tcx, closure_span) {
|
if !enable_precise_capture(cx.tcx, closure_span) {
|
||||||
bug!(
|
bug!(
|
||||||
"No associated capture found for {:?}[{:#?}] even though \
|
"No associated capture found for {:?}[{:#?}] even though \
|
||||||
capture_disjoint_fields isn't enabled",
|
capture_disjoint_fields isn't enabled",
|
||||||
|
@ -242,8 +245,8 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
|
||||||
};
|
};
|
||||||
|
|
||||||
// We won't be building MIR if the closure wasn't local
|
// We won't be building MIR if the closure wasn't local
|
||||||
let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local());
|
let closure_hir_id = cx.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local());
|
||||||
let closure_ty = typeck_results.node_type(closure_hir_id);
|
let closure_ty = cx.typeck_results.node_type(closure_hir_id);
|
||||||
|
|
||||||
let substs = match closure_ty.kind() {
|
let substs = match closure_ty.kind() {
|
||||||
ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
|
ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
|
||||||
|
@ -270,12 +273,14 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
|
||||||
|
|
||||||
// We used some of the projections to build the capture itself,
|
// We used some of the projections to build the capture itself,
|
||||||
// now we apply the remaining to the upvar resolved place.
|
// now we apply the remaining to the upvar resolved place.
|
||||||
|
trace!(?capture.place, ?from_builder.projection);
|
||||||
let remaining_projections = strip_prefix(
|
let remaining_projections = strip_prefix(
|
||||||
capture.place.base_ty,
|
capture.place.base_ty,
|
||||||
from_builder.projection,
|
from_builder.projection,
|
||||||
&capture.place.projections,
|
&capture.place.projections,
|
||||||
);
|
);
|
||||||
upvar_resolved_place_builder.projection.extend(remaining_projections);
|
upvar_resolved_place_builder.projection.extend(remaining_projections);
|
||||||
|
trace!(?upvar_resolved_place_builder);
|
||||||
|
|
||||||
Ok(upvar_resolved_place_builder)
|
Ok(upvar_resolved_place_builder)
|
||||||
}
|
}
|
||||||
|
@ -294,16 +299,21 @@ fn strip_prefix<'tcx>(
|
||||||
prefix_projections: &[HirProjection<'tcx>],
|
prefix_projections: &[HirProjection<'tcx>],
|
||||||
) -> impl Iterator<Item = PlaceElem<'tcx>> {
|
) -> impl Iterator<Item = PlaceElem<'tcx>> {
|
||||||
let mut iter = projections.into_iter();
|
let mut iter = projections.into_iter();
|
||||||
|
let mut next = || match iter.next()? {
|
||||||
|
// Filter out opaque casts, they are unnecessary in the prefix.
|
||||||
|
ProjectionElem::OpaqueCast(..) => iter.next(),
|
||||||
|
other => Some(other),
|
||||||
|
};
|
||||||
for projection in prefix_projections {
|
for projection in prefix_projections {
|
||||||
match projection.kind {
|
match projection.kind {
|
||||||
HirProjectionKind::Deref => {
|
HirProjectionKind::Deref => {
|
||||||
assert!(matches!(iter.next(), Some(ProjectionElem::Deref)));
|
assert!(matches!(next(), Some(ProjectionElem::Deref)));
|
||||||
}
|
}
|
||||||
HirProjectionKind::Field(..) => {
|
HirProjectionKind::Field(..) => {
|
||||||
if base_ty.is_enum() {
|
if base_ty.is_enum() {
|
||||||
assert!(matches!(iter.next(), Some(ProjectionElem::Downcast(..))));
|
assert!(matches!(next(), Some(ProjectionElem::Downcast(..))));
|
||||||
}
|
}
|
||||||
assert!(matches!(iter.next(), Some(ProjectionElem::Field(..))));
|
assert!(matches!(next(), Some(ProjectionElem::Field(..))));
|
||||||
}
|
}
|
||||||
HirProjectionKind::Index | HirProjectionKind::Subslice => {
|
HirProjectionKind::Index | HirProjectionKind::Subslice => {
|
||||||
bug!("unexpected projection kind: {:?}", projection);
|
bug!("unexpected projection kind: {:?}", projection);
|
||||||
|
@ -315,24 +325,32 @@ fn strip_prefix<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> PlaceBuilder<'tcx> {
|
impl<'tcx> PlaceBuilder<'tcx> {
|
||||||
pub(crate) fn into_place<'a>(
|
pub(crate) fn into_place(self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> {
|
||||||
self,
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
|
||||||
) -> Place<'tcx> {
|
|
||||||
if let PlaceBase::Local(local) = self.base {
|
if let PlaceBase::Local(local) = self.base {
|
||||||
Place { local, projection: tcx.intern_place_elems(&self.projection) }
|
let mut projections = vec![];
|
||||||
|
let mut ty = PlaceTy::from_ty(cx.local_decls[local].ty);
|
||||||
|
for projection in self.projection {
|
||||||
|
// Only preserve those opaque casts that actually go from an opaque type
|
||||||
|
// to another type.
|
||||||
|
if let ProjectionElem::OpaqueCast(t) = projection {
|
||||||
|
if let ty::Opaque(..) = ty.ty.kind() {
|
||||||
|
if t != ty.ty {
|
||||||
|
projections.push(ProjectionElem::OpaqueCast(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
projections.push(projection);
|
||||||
|
}
|
||||||
|
ty = ty.projection_ty(cx.tcx, projection);
|
||||||
|
}
|
||||||
|
Place { local, projection: cx.tcx.intern_place_elems(&projections) }
|
||||||
} else {
|
} else {
|
||||||
self.expect_upvars_resolved(tcx, typeck_results).into_place(tcx, typeck_results)
|
self.expect_upvars_resolved(cx).into_place(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect_upvars_resolved<'a>(
|
fn expect_upvars_resolved(self, cx: &Builder<'_, 'tcx>) -> PlaceBuilder<'tcx> {
|
||||||
self,
|
to_upvars_resolved_place_builder(self, cx).unwrap()
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
|
||||||
) -> PlaceBuilder<'tcx> {
|
|
||||||
to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to resolve the `PlaceBuilder`.
|
/// Attempts to resolve the `PlaceBuilder`.
|
||||||
|
@ -346,12 +364,11 @@ impl<'tcx> PlaceBuilder<'tcx> {
|
||||||
/// not captured. This can happen because the final mir that will be
|
/// not captured. This can happen because the final mir that will be
|
||||||
/// generated doesn't require a read for this place. Failures will only
|
/// generated doesn't require a read for this place. Failures will only
|
||||||
/// happen inside closures.
|
/// happen inside closures.
|
||||||
pub(crate) fn try_upvars_resolved<'a>(
|
pub(crate) fn try_upvars_resolved(
|
||||||
self,
|
self,
|
||||||
tcx: TyCtxt<'tcx>,
|
cx: &Builder<'_, 'tcx>,
|
||||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
|
||||||
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
|
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
|
||||||
to_upvars_resolved_place_builder(self, tcx, typeck_results)
|
to_upvars_resolved_place_builder(self, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn base(&self) -> PlaceBase {
|
pub(crate) fn base(&self) -> PlaceBase {
|
||||||
|
@ -411,7 +428,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
expr: &Expr<'tcx>,
|
expr: &Expr<'tcx>,
|
||||||
) -> BlockAnd<Place<'tcx>> {
|
) -> BlockAnd<Place<'tcx>> {
|
||||||
let place_builder = unpack!(block = self.as_place_builder(block, expr));
|
let place_builder = unpack!(block = self.as_place_builder(block, expr));
|
||||||
block.and(place_builder.into_place(self.tcx, self.typeck_results))
|
block.and(place_builder.into_place(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is used when constructing a compound `Place`, so that we can avoid creating
|
/// This is used when constructing a compound `Place`, so that we can avoid creating
|
||||||
|
@ -435,7 +452,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
expr: &Expr<'tcx>,
|
expr: &Expr<'tcx>,
|
||||||
) -> BlockAnd<Place<'tcx>> {
|
) -> BlockAnd<Place<'tcx>> {
|
||||||
let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
|
let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
|
||||||
block.and(place_builder.into_place(self.tcx, self.typeck_results))
|
block.and(place_builder.into_place(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is used when constructing a compound `Place`, so that we can avoid creating
|
/// This is used when constructing a compound `Place`, so that we can avoid creating
|
||||||
|
@ -530,7 +547,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
inferred_ty: expr.ty,
|
inferred_ty: expr.ty,
|
||||||
});
|
});
|
||||||
|
|
||||||
let place = place_builder.clone().into_place(this.tcx, this.typeck_results);
|
let place = place_builder.clone().into_place(this);
|
||||||
this.cfg.push(
|
this.cfg.push(
|
||||||
block,
|
block,
|
||||||
Statement {
|
Statement {
|
||||||
|
@ -682,7 +699,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
if is_outermost_index {
|
if is_outermost_index {
|
||||||
self.read_fake_borrows(block, fake_borrow_temps, source_info)
|
self.read_fake_borrows(block, fake_borrow_temps, source_info)
|
||||||
} else {
|
} else {
|
||||||
base_place = base_place.expect_upvars_resolved(self.tcx, self.typeck_results);
|
base_place = base_place.expect_upvars_resolved(self);
|
||||||
self.add_fake_borrows_of_base(
|
self.add_fake_borrows_of_base(
|
||||||
&base_place,
|
&base_place,
|
||||||
block,
|
block,
|
||||||
|
@ -710,12 +727,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
let lt = self.temp(bool_ty, expr_span);
|
let lt = self.temp(bool_ty, expr_span);
|
||||||
|
|
||||||
// len = len(slice)
|
// len = len(slice)
|
||||||
self.cfg.push_assign(
|
self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.into_place(self)));
|
||||||
block,
|
|
||||||
source_info,
|
|
||||||
len,
|
|
||||||
Rvalue::Len(slice.into_place(self.tcx, self.typeck_results)),
|
|
||||||
);
|
|
||||||
// lt = idx < len
|
// lt = idx < len
|
||||||
self.cfg.push_assign(
|
self.cfg.push_assign(
|
||||||
block,
|
block,
|
||||||
|
@ -795,6 +807,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
ProjectionElem::Field(..)
|
ProjectionElem::Field(..)
|
||||||
| ProjectionElem::Downcast(..)
|
| ProjectionElem::Downcast(..)
|
||||||
|
| ProjectionElem::OpaqueCast(..)
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
| ProjectionElem::Subslice { .. } => (),
|
| ProjectionElem::Subslice { .. } => (),
|
||||||
}
|
}
|
||||||
|
|
|
@ -321,11 +321,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
let place_builder =
|
let place_builder =
|
||||||
unpack!(block = this.as_place_builder(block, &this.thir[*thir_place]));
|
unpack!(block = this.as_place_builder(block, &this.thir[*thir_place]));
|
||||||
|
|
||||||
if let Ok(place_builder_resolved) =
|
if let Ok(place_builder_resolved) = place_builder.try_upvars_resolved(this) {
|
||||||
place_builder.try_upvars_resolved(this.tcx, this.typeck_results)
|
let mir_place = place_builder_resolved.into_place(this);
|
||||||
{
|
|
||||||
let mir_place =
|
|
||||||
place_builder_resolved.into_place(this.tcx, this.typeck_results);
|
|
||||||
this.cfg.push_fake_read(
|
this.cfg.push_fake_read(
|
||||||
block,
|
block,
|
||||||
this.source_info(this.tcx.hir().span(*hir_id)),
|
this.source_info(this.tcx.hir().span(*hir_id)),
|
||||||
|
@ -616,8 +613,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
// by the parent itself. The mutability of the current capture
|
// by the parent itself. The mutability of the current capture
|
||||||
// is same as that of the capture in the parent closure.
|
// is same as that of the capture in the parent closure.
|
||||||
PlaceBase::Upvar { .. } => {
|
PlaceBase::Upvar { .. } => {
|
||||||
let enclosing_upvars_resolved =
|
let enclosing_upvars_resolved = arg_place_builder.clone().into_place(this);
|
||||||
arg_place_builder.clone().into_place(this.tcx, this.typeck_results);
|
|
||||||
|
|
||||||
match enclosing_upvars_resolved.as_ref() {
|
match enclosing_upvars_resolved.as_ref() {
|
||||||
PlaceRef {
|
PlaceRef {
|
||||||
|
@ -654,7 +650,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
|
Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
|
||||||
};
|
};
|
||||||
|
|
||||||
let arg_place = arg_place_builder.into_place(this.tcx, this.typeck_results);
|
let arg_place = arg_place_builder.into_place(this);
|
||||||
|
|
||||||
this.cfg.push_assign(
|
this.cfg.push_assign(
|
||||||
block,
|
block,
|
||||||
|
|
|
@ -23,6 +23,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr, mutability))
|
ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr, mutability))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self), level = "debug")]
|
||||||
fn as_temp_inner(
|
fn as_temp_inner(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut block: BasicBlock,
|
mut block: BasicBlock,
|
||||||
|
@ -30,10 +31,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
expr: &Expr<'tcx>,
|
expr: &Expr<'tcx>,
|
||||||
mutability: Mutability,
|
mutability: Mutability,
|
||||||
) -> BlockAnd<Local> {
|
) -> BlockAnd<Local> {
|
||||||
debug!(
|
|
||||||
"as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
|
|
||||||
block, temp_lifetime, expr, mutability
|
|
||||||
);
|
|
||||||
let this = self;
|
let this = self;
|
||||||
|
|
||||||
let expr_span = expr.span;
|
let expr_span = expr.span;
|
||||||
|
|
|
@ -15,14 +15,13 @@ use std::iter;
|
||||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
/// Compile `expr`, storing the result into `destination`, which
|
/// Compile `expr`, storing the result into `destination`, which
|
||||||
/// is assumed to be uninitialized.
|
/// is assumed to be uninitialized.
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
pub(crate) fn expr_into_dest(
|
pub(crate) fn expr_into_dest(
|
||||||
&mut self,
|
&mut self,
|
||||||
destination: Place<'tcx>,
|
destination: Place<'tcx>,
|
||||||
mut block: BasicBlock,
|
mut block: BasicBlock,
|
||||||
expr: &Expr<'tcx>,
|
expr: &Expr<'tcx>,
|
||||||
) -> BlockAnd<()> {
|
) -> BlockAnd<()> {
|
||||||
debug!("expr_into_dest(destination={:?}, block={:?}, expr={:?})", destination, block, expr);
|
|
||||||
|
|
||||||
// since we frequently have to reference `self` from within a
|
// since we frequently have to reference `self` from within a
|
||||||
// closure, where `self` would be shadowed, it's easier to
|
// closure, where `self` would be shadowed, it's easier to
|
||||||
// just use the name `this` uniformly
|
// just use the name `this` uniformly
|
||||||
|
@ -366,9 +365,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
None => {
|
None => {
|
||||||
let place_builder = place_builder.clone();
|
let place_builder = place_builder.clone();
|
||||||
this.consume_by_copy_or_move(
|
this.consume_by_copy_or_move(
|
||||||
place_builder
|
place_builder.field(n, *ty).into_place(this),
|
||||||
.field(n, *ty)
|
|
||||||
.into_place(this.tcx, this.typeck_results),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -220,10 +220,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
|
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
|
||||||
let source_info = self.source_info(scrutinee_span);
|
let source_info = self.source_info(scrutinee_span);
|
||||||
|
|
||||||
if let Ok(scrutinee_builder) =
|
if let Ok(scrutinee_builder) = scrutinee_place_builder.clone().try_upvars_resolved(self) {
|
||||||
scrutinee_place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
let scrutinee_place = scrutinee_builder.into_place(self);
|
||||||
{
|
|
||||||
let scrutinee_place = scrutinee_builder.into_place(self.tcx, self.typeck_results);
|
|
||||||
self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
|
self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,12 +346,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
// ```
|
// ```
|
||||||
let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None;
|
let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None;
|
||||||
let scrutinee_place: Place<'tcx>;
|
let scrutinee_place: Place<'tcx>;
|
||||||
if let Ok(scrutinee_builder) = scrutinee_place_builder
|
if let Ok(scrutinee_builder) =
|
||||||
.clone()
|
scrutinee_place_builder.clone().try_upvars_resolved(this)
|
||||||
.try_upvars_resolved(this.tcx, this.typeck_results)
|
|
||||||
{
|
{
|
||||||
scrutinee_place =
|
scrutinee_place = scrutinee_builder.into_place(this);
|
||||||
scrutinee_builder.into_place(this.tcx, this.typeck_results);
|
|
||||||
opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span));
|
opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span));
|
||||||
}
|
}
|
||||||
let scope = this.declare_bindings(
|
let scope = this.declare_bindings(
|
||||||
|
@ -602,12 +598,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
while let Some(next) = {
|
while let Some(next) = {
|
||||||
for binding in &candidate_ref.bindings {
|
for binding in &candidate_ref.bindings {
|
||||||
let local = self.var_local_id(binding.var_id, OutsideGuard);
|
let local = self.var_local_id(binding.var_id, OutsideGuard);
|
||||||
|
|
||||||
let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
|
|
||||||
VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
|
|
||||||
)))) = self.local_decls[local].local_info else {
|
|
||||||
bug!("Let binding to non-user variable.")
|
|
||||||
};
|
|
||||||
// `try_upvars_resolved` may fail if it is unable to resolve the given
|
// `try_upvars_resolved` may fail if it is unable to resolve the given
|
||||||
// `PlaceBuilder` inside a closure. In this case, we don't want to include
|
// `PlaceBuilder` inside a closure. In this case, we don't want to include
|
||||||
// a scrutinee place. `scrutinee_place_builder` will fail for destructured
|
// a scrutinee place. `scrutinee_place_builder` will fail for destructured
|
||||||
|
@ -622,10 +612,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
// let (v1, v2) = foo;
|
// let (v1, v2) = foo;
|
||||||
// };
|
// };
|
||||||
// ```
|
// ```
|
||||||
if let Ok(match_pair_resolved) =
|
if let Ok(match_pair_resolved) = initializer.clone().try_upvars_resolved(self) {
|
||||||
initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
let place = match_pair_resolved.into_place(self);
|
||||||
{
|
|
||||||
let place = match_pair_resolved.into_place(self.tcx, self.typeck_results);
|
let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
|
||||||
|
VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
|
||||||
|
)))) = self.local_decls[local].local_info else {
|
||||||
|
bug!("Let binding to non-user variable.")
|
||||||
|
};
|
||||||
|
|
||||||
*match_place = Some(place);
|
*match_place = Some(place);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -654,6 +649,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
/// scope for the bindings in these patterns, if such a scope had to be
|
/// scope for the bindings in these patterns, if such a scope had to be
|
||||||
/// created. NOTE: Declaring the bindings should always be done in their
|
/// created. NOTE: Declaring the bindings should always be done in their
|
||||||
/// drop scope.
|
/// drop scope.
|
||||||
|
#[instrument(skip(self), level = "debug")]
|
||||||
pub(crate) fn declare_bindings(
|
pub(crate) fn declare_bindings(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut visibility_scope: Option<SourceScope>,
|
mut visibility_scope: Option<SourceScope>,
|
||||||
|
@ -662,7 +658,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
has_guard: ArmHasGuard,
|
has_guard: ArmHasGuard,
|
||||||
opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,
|
opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,
|
||||||
) -> Option<SourceScope> {
|
) -> Option<SourceScope> {
|
||||||
debug!("declare_bindings: pattern={:?}", pattern);
|
|
||||||
self.visit_primary_bindings(
|
self.visit_primary_bindings(
|
||||||
&pattern,
|
&pattern,
|
||||||
UserTypeProjections::none(),
|
UserTypeProjections::none(),
|
||||||
|
@ -872,7 +867,7 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
|
||||||
Candidate {
|
Candidate {
|
||||||
span: pattern.span,
|
span: pattern.span,
|
||||||
has_guard,
|
has_guard,
|
||||||
match_pairs: smallvec![MatchPair { place, pattern }],
|
match_pairs: smallvec![MatchPair::new(place, pattern)],
|
||||||
bindings: Vec::new(),
|
bindings: Vec::new(),
|
||||||
ascriptions: Vec::new(),
|
ascriptions: Vec::new(),
|
||||||
subcandidates: Vec::new(),
|
subcandidates: Vec::new(),
|
||||||
|
@ -1048,6 +1043,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
/// if `x.0` matches `false` (for the third arm). In the (impossible at
|
/// if `x.0` matches `false` (for the third arm). In the (impossible at
|
||||||
/// runtime) case when `x.0` is now `true`, we branch to
|
/// runtime) case when `x.0` is now `true`, we branch to
|
||||||
/// `otherwise_block`.
|
/// `otherwise_block`.
|
||||||
|
#[instrument(skip(self, fake_borrows), level = "debug")]
|
||||||
fn match_candidates<'pat>(
|
fn match_candidates<'pat>(
|
||||||
&mut self,
|
&mut self,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
@ -1057,11 +1053,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
candidates: &mut [&mut Candidate<'pat, 'tcx>],
|
candidates: &mut [&mut Candidate<'pat, 'tcx>],
|
||||||
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
|
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
|
||||||
) {
|
) {
|
||||||
debug!(
|
|
||||||
"matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})",
|
|
||||||
span, candidates, start_block, otherwise_block,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Start by simplifying candidates. Once this process is complete, all
|
// Start by simplifying candidates. Once this process is complete, all
|
||||||
// the match pairs which remain require some form of test, whether it
|
// the match pairs which remain require some form of test, whether it
|
||||||
// be a switch or pattern comparison.
|
// be a switch or pattern comparison.
|
||||||
|
@ -1380,6 +1371,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(
|
||||||
|
skip(self, otherwise, or_span, place, fake_borrows, candidate, pats),
|
||||||
|
level = "debug"
|
||||||
|
)]
|
||||||
fn test_or_pattern<'pat>(
|
fn test_or_pattern<'pat>(
|
||||||
&mut self,
|
&mut self,
|
||||||
candidate: &mut Candidate<'pat, 'tcx>,
|
candidate: &mut Candidate<'pat, 'tcx>,
|
||||||
|
@ -1389,7 +1384,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
place: PlaceBuilder<'tcx>,
|
place: PlaceBuilder<'tcx>,
|
||||||
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
|
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
|
||||||
) {
|
) {
|
||||||
debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats);
|
debug!("candidate={:#?}\npats={:#?}", candidate, pats);
|
||||||
let mut or_candidates: Vec<_> = pats
|
let mut or_candidates: Vec<_> = pats
|
||||||
.iter()
|
.iter()
|
||||||
.map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard))
|
.map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard))
|
||||||
|
@ -1605,9 +1600,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
|
|
||||||
// Insert a Shallow borrow of any places that is switched on.
|
// Insert a Shallow borrow of any places that is switched on.
|
||||||
if let Some(fb) = fake_borrows && let Ok(match_place_resolved) =
|
if let Some(fb) = fake_borrows && let Ok(match_place_resolved) =
|
||||||
match_place.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
match_place.clone().try_upvars_resolved(self)
|
||||||
{
|
{
|
||||||
let resolved_place = match_place_resolved.into_place(self.tcx, self.typeck_results);
|
let resolved_place = match_place_resolved.into_place(self);
|
||||||
fb.insert(resolved_place);
|
fb.insert(resolved_place);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1634,9 +1629,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
candidates = rest;
|
candidates = rest;
|
||||||
}
|
}
|
||||||
// at least the first candidate ought to be tested
|
// at least the first candidate ought to be tested
|
||||||
assert!(total_candidate_count > candidates.len());
|
assert!(
|
||||||
debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len());
|
total_candidate_count > candidates.len(),
|
||||||
debug!("test_candidates: untested_candidates: {}", candidates.len());
|
"{}, {:#?}",
|
||||||
|
total_candidate_count,
|
||||||
|
candidates
|
||||||
|
);
|
||||||
|
debug!("tested_candidates: {}", total_candidate_count - candidates.len());
|
||||||
|
debug!("untested_candidates: {}", candidates.len());
|
||||||
|
|
||||||
// HACK(matthewjasper) This is a closure so that we can let the test
|
// HACK(matthewjasper) This is a closure so that we can let the test
|
||||||
// create its blocks before the rest of the match. This currently
|
// create its blocks before the rest of the match. This currently
|
||||||
|
@ -1794,10 +1794,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
let mut opt_expr_place: Option<(Option<&Place<'tcx>>, Span)> = None;
|
let mut opt_expr_place: Option<(Option<&Place<'tcx>>, Span)> = None;
|
||||||
let expr_place: Place<'tcx>;
|
let expr_place: Place<'tcx>;
|
||||||
if let Ok(expr_builder) =
|
if let Ok(expr_builder) = expr_place_builder.try_upvars_resolved(self) {
|
||||||
expr_place_builder.try_upvars_resolved(self.tcx, self.typeck_results)
|
expr_place = expr_builder.into_place(self);
|
||||||
{
|
|
||||||
expr_place = expr_builder.into_place(self.tcx, self.typeck_results);
|
|
||||||
opt_expr_place = Some((Some(&expr_place), expr_span));
|
opt_expr_place = Some((Some(&expr_place), expr_span));
|
||||||
}
|
}
|
||||||
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
|
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
|
||||||
|
@ -2195,6 +2193,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
/// first local is a binding for occurrences of `var` in the guard, which
|
/// first local is a binding for occurrences of `var` in the guard, which
|
||||||
/// will have type `&T`. The second local is a binding for occurrences of
|
/// will have type `&T`. The second local is a binding for occurrences of
|
||||||
/// `var` in the arm body, which will have type `T`.
|
/// `var` in the arm body, which will have type `T`.
|
||||||
|
#[instrument(skip(self), level = "debug")]
|
||||||
fn declare_binding(
|
fn declare_binding(
|
||||||
&mut self,
|
&mut self,
|
||||||
source_info: SourceInfo,
|
source_info: SourceInfo,
|
||||||
|
@ -2209,19 +2208,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
|
opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
|
||||||
pat_span: Span,
|
pat_span: Span,
|
||||||
) {
|
) {
|
||||||
debug!(
|
|
||||||
"declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \
|
|
||||||
visibility_scope={:?}, source_info={:?})",
|
|
||||||
var_id, name, mode, var_ty, visibility_scope, source_info
|
|
||||||
);
|
|
||||||
|
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope };
|
let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope };
|
||||||
let binding_mode = match mode {
|
let binding_mode = match mode {
|
||||||
BindingMode::ByValue => ty::BindingMode::BindByValue(mutability),
|
BindingMode::ByValue => ty::BindingMode::BindByValue(mutability),
|
||||||
BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability),
|
BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability),
|
||||||
};
|
};
|
||||||
debug!("declare_binding: user_ty={:?}", user_ty);
|
|
||||||
let local = LocalDecl::<'tcx> {
|
let local = LocalDecl::<'tcx> {
|
||||||
mutability,
|
mutability,
|
||||||
ty: var_ty,
|
ty: var_ty,
|
||||||
|
@ -2271,7 +2263,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
} else {
|
} else {
|
||||||
LocalsForNode::One(for_arm_body)
|
LocalsForNode::One(for_arm_body)
|
||||||
};
|
};
|
||||||
debug!("declare_binding: vars={:?}", locals);
|
debug!(?locals);
|
||||||
self.var_indices.insert(var_id, locals);
|
self.var_indices.insert(var_id, locals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,12 +37,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
///
|
///
|
||||||
/// only generates a single switch. If this happens this method returns
|
/// only generates a single switch. If this happens this method returns
|
||||||
/// `true`.
|
/// `true`.
|
||||||
|
#[instrument(skip(self, candidate), level = "debug")]
|
||||||
pub(super) fn simplify_candidate<'pat>(
|
pub(super) fn simplify_candidate<'pat>(
|
||||||
&mut self,
|
&mut self,
|
||||||
candidate: &mut Candidate<'pat, 'tcx>,
|
candidate: &mut Candidate<'pat, 'tcx>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// repeatedly simplify match pairs until fixed point is reached
|
// repeatedly simplify match pairs until fixed point is reached
|
||||||
debug!(?candidate, "simplify_candidate");
|
debug!("{:#?}", candidate);
|
||||||
|
|
||||||
// existing_bindings and new_bindings exists to keep the semantics in order.
|
// existing_bindings and new_bindings exists to keep the semantics in order.
|
||||||
// Reversing the binding order for bindings after `@` changes the binding order in places
|
// Reversing the binding order for bindings after `@` changes the binding order in places
|
||||||
|
@ -155,12 +156,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
ascription: thir::Ascription { ref annotation, variance },
|
ascription: thir::Ascription { ref annotation, variance },
|
||||||
} => {
|
} => {
|
||||||
// Apply the type ascription to the value at `match_pair.place`, which is the
|
// Apply the type ascription to the value at `match_pair.place`, which is the
|
||||||
if let Ok(place_resolved) =
|
if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self) {
|
||||||
match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
|
||||||
{
|
|
||||||
candidate.ascriptions.push(Ascription {
|
candidate.ascriptions.push(Ascription {
|
||||||
annotation: annotation.clone(),
|
annotation: annotation.clone(),
|
||||||
source: place_resolved.into_place(self.tcx, self.typeck_results),
|
source: place_resolved.into_place(self),
|
||||||
variance,
|
variance,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -184,12 +183,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
ref subpattern,
|
ref subpattern,
|
||||||
is_primary: _,
|
is_primary: _,
|
||||||
} => {
|
} => {
|
||||||
if let Ok(place_resolved) =
|
if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self) {
|
||||||
match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
|
||||||
{
|
|
||||||
candidate.bindings.push(Binding {
|
candidate.bindings.push(Binding {
|
||||||
span: match_pair.pattern.span,
|
span: match_pair.pattern.span,
|
||||||
source: place_resolved.into_place(self.tcx, self.typeck_results),
|
source: place_resolved.into_place(self),
|
||||||
var_id: var,
|
var_id: var,
|
||||||
binding_mode: mode,
|
binding_mode: mode,
|
||||||
});
|
});
|
||||||
|
|
|
@ -144,6 +144,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self, make_target_blocks, place_builder), level = "debug")]
|
||||||
pub(super) fn perform_test(
|
pub(super) fn perform_test(
|
||||||
&mut self,
|
&mut self,
|
||||||
match_start_span: Span,
|
match_start_span: Span,
|
||||||
|
@ -153,21 +154,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
test: &Test<'tcx>,
|
test: &Test<'tcx>,
|
||||||
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
|
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
|
||||||
) {
|
) {
|
||||||
let place: Place<'tcx>;
|
let place = place_builder.into_place(self);
|
||||||
if let Ok(test_place_builder) =
|
let place_ty = place.ty(&self.local_decls, self.tcx);
|
||||||
place_builder.try_upvars_resolved(self.tcx, self.typeck_results)
|
debug!(?place, ?place_ty,);
|
||||||
{
|
|
||||||
place = test_place_builder.into_place(self.tcx, self.typeck_results);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
debug!(
|
|
||||||
"perform_test({:?}, {:?}: {:?}, {:?})",
|
|
||||||
block,
|
|
||||||
place,
|
|
||||||
place.ty(&self.local_decls, self.tcx),
|
|
||||||
test
|
|
||||||
);
|
|
||||||
|
|
||||||
let source_info = self.source_info(test.span);
|
let source_info = self.source_info(test.span);
|
||||||
match test.kind {
|
match test.kind {
|
||||||
|
@ -735,9 +724,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
// So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
|
// So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
|
||||||
// we want to create a set of derived match-patterns like
|
// we want to create a set of derived match-patterns like
|
||||||
// `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
|
// `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
|
||||||
let elem =
|
let downcast_place = match_pair.place.downcast(adt_def, variant_index); // `(x as Variant)`
|
||||||
ProjectionElem::Downcast(Some(adt_def.variant(variant_index).name), variant_index);
|
|
||||||
let downcast_place = match_pair.place.project(elem); // `(x as Variant)`
|
|
||||||
let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
|
let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
|
||||||
// e.g., `(x as Variant).0`
|
// e.g., `(x as Variant).0`
|
||||||
let place = downcast_place.clone().field(subpattern.field, subpattern.pattern.ty);
|
let place = downcast_place.clone().field(subpattern.field, subpattern.pattern.ty);
|
||||||
|
|
|
@ -31,21 +31,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
suffix: &'pat [Pat<'tcx>],
|
suffix: &'pat [Pat<'tcx>],
|
||||||
) {
|
) {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let (min_length, exact_size) = if let Ok(place_resolved) =
|
let (min_length, exact_size) =
|
||||||
place.clone().try_upvars_resolved(tcx, self.typeck_results)
|
if let Ok(place_resolved) = place.clone().try_upvars_resolved(self) {
|
||||||
{
|
match place_resolved.into_place(self).ty(&self.local_decls, tcx).ty.kind() {
|
||||||
match place_resolved
|
ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true),
|
||||||
.into_place(tcx, self.typeck_results)
|
_ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
|
||||||
.ty(&self.local_decls, tcx)
|
}
|
||||||
.ty
|
} else {
|
||||||
.kind()
|
((prefix.len() + suffix.len()).try_into().unwrap(), false)
|
||||||
{
|
};
|
||||||
ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true),
|
|
||||||
_ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
((prefix.len() + suffix.len()).try_into().unwrap(), false)
|
|
||||||
};
|
|
||||||
|
|
||||||
match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
|
match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
|
||||||
let elem =
|
let elem =
|
||||||
|
@ -100,10 +94,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
|
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
|
||||||
pub(crate) fn new(
|
pub(in crate::build) fn new(
|
||||||
place: PlaceBuilder<'tcx>,
|
place: PlaceBuilder<'tcx>,
|
||||||
pattern: &'pat Pat<'tcx>,
|
pattern: &'pat Pat<'tcx>,
|
||||||
) -> MatchPair<'pat, 'tcx> {
|
) -> MatchPair<'pat, 'tcx> {
|
||||||
|
// Force the place type to the pattern's type.
|
||||||
|
// FIXME(oli-obk): only do this when we don't already know the place type.
|
||||||
|
// FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
|
||||||
|
let place = place.project(ProjectionElem::OpaqueCast(pattern.ty));
|
||||||
MatchPair { place, pattern }
|
MatchPair { place, pattern }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -553,6 +553,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
|
|
||||||
/// Convenience wrapper that pushes a scope and then executes `f`
|
/// Convenience wrapper that pushes a scope and then executes `f`
|
||||||
/// to build its contents, popping the scope afterwards.
|
/// to build its contents, popping the scope afterwards.
|
||||||
|
#[instrument(skip(self, f), level = "debug")]
|
||||||
pub(crate) fn in_scope<F, R>(
|
pub(crate) fn in_scope<F, R>(
|
||||||
&mut self,
|
&mut self,
|
||||||
region_scope: (region::Scope, SourceInfo),
|
region_scope: (region::Scope, SourceInfo),
|
||||||
|
@ -562,7 +563,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
|
F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
|
||||||
{
|
{
|
||||||
debug!("in_scope(region_scope={:?})", region_scope);
|
|
||||||
let source_scope = self.source_scope;
|
let source_scope = self.source_scope;
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
if let LintLevel::Explicit(current_hir_id) = lint_level {
|
if let LintLevel::Explicit(current_hir_id) = lint_level {
|
||||||
|
@ -589,7 +589,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
let rv = unpack!(block = f(self));
|
let rv = unpack!(block = f(self));
|
||||||
unpack!(block = self.pop_scope(region_scope, block));
|
unpack!(block = self.pop_scope(region_scope, block));
|
||||||
self.source_scope = source_scope;
|
self.source_scope = source_scope;
|
||||||
debug!("in_scope: exiting region_scope={:?} block={:?}", region_scope, block);
|
debug!(?block);
|
||||||
block.and(rv)
|
block.and(rv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,8 @@ impl<'tcx> Cx<'tcx> {
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
trace!(?expr.ty);
|
||||||
|
|
||||||
// Now apply adjustments, if any.
|
// Now apply adjustments, if any.
|
||||||
for adjustment in self.typeck_results.expr_adjustments(hir_expr) {
|
for adjustment in self.typeck_results.expr_adjustments(hir_expr) {
|
||||||
trace!(?expr, ?adjustment);
|
trace!(?expr, ?adjustment);
|
||||||
|
@ -56,6 +58,8 @@ impl<'tcx> Cx<'tcx> {
|
||||||
self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span));
|
self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace!(?expr.ty, "after adjustments");
|
||||||
|
|
||||||
// Next, wrap this up in the expr's scope.
|
// Next, wrap this up in the expr's scope.
|
||||||
expr = Expr {
|
expr = Expr {
|
||||||
temp_lifetime,
|
temp_lifetime,
|
||||||
|
|
|
@ -1202,35 +1202,32 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
|
||||||
|
|
||||||
/// Creates a new list of wildcard fields for a given constructor. The result must have a
|
/// Creates a new list of wildcard fields for a given constructor. The result must have a
|
||||||
/// length of `constructor.arity()`.
|
/// length of `constructor.arity()`.
|
||||||
pub(super) fn wildcards(
|
#[instrument(level = "trace")]
|
||||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
pub(super) fn wildcards(pcx: PatCtxt<'_, 'p, 'tcx>, constructor: &Constructor<'tcx>) -> Self {
|
||||||
ty: Ty<'tcx>,
|
|
||||||
constructor: &Constructor<'tcx>,
|
|
||||||
) -> Self {
|
|
||||||
let ret = match constructor {
|
let ret = match constructor {
|
||||||
Single | Variant(_) => match ty.kind() {
|
Single | Variant(_) => match pcx.ty.kind() {
|
||||||
ty::Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter()),
|
ty::Tuple(fs) => Fields::wildcards_from_tys(pcx.cx, fs.iter()),
|
||||||
ty::Ref(_, rty, _) => Fields::wildcards_from_tys(cx, once(*rty)),
|
ty::Ref(_, rty, _) => Fields::wildcards_from_tys(pcx.cx, once(*rty)),
|
||||||
ty::Adt(adt, substs) => {
|
ty::Adt(adt, substs) => {
|
||||||
if adt.is_box() {
|
if adt.is_box() {
|
||||||
// The only legal patterns of type `Box` (outside `std`) are `_` and box
|
// The only legal patterns of type `Box` (outside `std`) are `_` and box
|
||||||
// patterns. If we're here we can assume this is a box pattern.
|
// patterns. If we're here we can assume this is a box pattern.
|
||||||
Fields::wildcards_from_tys(cx, once(substs.type_at(0)))
|
Fields::wildcards_from_tys(pcx.cx, once(substs.type_at(0)))
|
||||||
} else {
|
} else {
|
||||||
let variant = &adt.variant(constructor.variant_index_for_adt(*adt));
|
let variant = &adt.variant(constructor.variant_index_for_adt(*adt));
|
||||||
let tys = Fields::list_variant_nonhidden_fields(cx, ty, variant)
|
let tys = Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant)
|
||||||
.map(|(_, ty)| ty);
|
.map(|(_, ty)| ty);
|
||||||
Fields::wildcards_from_tys(cx, tys)
|
Fields::wildcards_from_tys(pcx.cx, tys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => bug!("Unexpected type for `Single` constructor: {:?}", ty),
|
_ => bug!("Unexpected type for `Single` constructor: {:?}", pcx),
|
||||||
},
|
},
|
||||||
Slice(slice) => match *ty.kind() {
|
Slice(slice) => match *pcx.ty.kind() {
|
||||||
ty::Slice(ty) | ty::Array(ty, _) => {
|
ty::Slice(ty) | ty::Array(ty, _) => {
|
||||||
let arity = slice.arity();
|
let arity = slice.arity();
|
||||||
Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty))
|
Fields::wildcards_from_tys(pcx.cx, (0..arity).map(|_| ty))
|
||||||
}
|
}
|
||||||
_ => bug!("bad slice pattern {:?} {:?}", constructor, ty),
|
_ => bug!("bad slice pattern {:?} {:?}", constructor, pcx),
|
||||||
},
|
},
|
||||||
Str(..)
|
Str(..)
|
||||||
| FloatRange(..)
|
| FloatRange(..)
|
||||||
|
@ -1243,7 +1240,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
|
||||||
bug!("called `Fields::wildcards` on an `Or` ctor")
|
bug!("called `Fields::wildcards` on an `Or` ctor")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
|
debug!(?ret);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1286,7 +1283,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
|
||||||
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
|
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
|
||||||
/// `Some(_)`.
|
/// `Some(_)`.
|
||||||
pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self {
|
pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self {
|
||||||
let fields = Fields::wildcards(pcx.cx, pcx.ty, &ctor);
|
let fields = Fields::wildcards(pcx, &ctor);
|
||||||
DeconstructedPat::new(ctor, fields, pcx.ty, DUMMY_SP)
|
DeconstructedPat::new(ctor, fields, pcx.ty, DUMMY_SP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1553,13 +1550,13 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
|
||||||
/// `other_ctor` can be different from `self.ctor`, but must be covered by it.
|
/// `other_ctor` can be different from `self.ctor`, but must be covered by it.
|
||||||
pub(super) fn specialize<'a>(
|
pub(super) fn specialize<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
pcx: PatCtxt<'_, 'p, 'tcx>,
|
||||||
other_ctor: &Constructor<'tcx>,
|
other_ctor: &Constructor<'tcx>,
|
||||||
) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> {
|
) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> {
|
||||||
match (&self.ctor, other_ctor) {
|
match (&self.ctor, other_ctor) {
|
||||||
(Wildcard, _) => {
|
(Wildcard, _) => {
|
||||||
// We return a wildcard for each field of `other_ctor`.
|
// We return a wildcard for each field of `other_ctor`.
|
||||||
Fields::wildcards(cx, self.ty, other_ctor).iter_patterns().collect()
|
Fields::wildcards(pcx, other_ctor).iter_patterns().collect()
|
||||||
}
|
}
|
||||||
(Slice(self_slice), Slice(other_slice))
|
(Slice(self_slice), Slice(other_slice))
|
||||||
if self_slice.arity() != other_slice.arity() =>
|
if self_slice.arity() != other_slice.arity() =>
|
||||||
|
@ -1578,7 +1575,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
|
||||||
let prefix = &self.fields.fields[..prefix];
|
let prefix = &self.fields.fields[..prefix];
|
||||||
let suffix = &self.fields.fields[self_slice.arity() - suffix..];
|
let suffix = &self.fields.fields[self_slice.arity() - suffix..];
|
||||||
let wildcard: &_ =
|
let wildcard: &_ =
|
||||||
cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty));
|
pcx.cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty));
|
||||||
let extra_wildcards = other_slice.arity() - self_slice.arity();
|
let extra_wildcards = other_slice.arity() - self_slice.arity();
|
||||||
let extra_wildcards = (0..extra_wildcards).map(|_| wildcard);
|
let extra_wildcards = (0..extra_wildcards).map(|_| wildcard);
|
||||||
prefix.iter().chain(extra_wildcards).chain(suffix).collect()
|
prefix.iter().chain(extra_wildcards).chain(suffix).collect()
|
||||||
|
|
|
@ -196,6 +196,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self), level = "debug")]
|
||||||
fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> {
|
fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> {
|
||||||
let mut ty = self.typeck_results.node_type(pat.hir_id);
|
let mut ty = self.typeck_results.node_type(pat.hir_id);
|
||||||
|
|
||||||
|
|
|
@ -411,12 +411,12 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
|
||||||
/// This is roughly the inverse of `Constructor::apply`.
|
/// This is roughly the inverse of `Constructor::apply`.
|
||||||
fn pop_head_constructor(
|
fn pop_head_constructor(
|
||||||
&self,
|
&self,
|
||||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
pcx: PatCtxt<'_, 'p, 'tcx>,
|
||||||
ctor: &Constructor<'tcx>,
|
ctor: &Constructor<'tcx>,
|
||||||
) -> PatStack<'p, 'tcx> {
|
) -> PatStack<'p, 'tcx> {
|
||||||
// We pop the head pattern and push the new fields extracted from the arguments of
|
// We pop the head pattern and push the new fields extracted from the arguments of
|
||||||
// `self.head()`.
|
// `self.head()`.
|
||||||
let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(cx, ctor);
|
let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(pcx, ctor);
|
||||||
new_fields.extend_from_slice(&self.pats[1..]);
|
new_fields.extend_from_slice(&self.pats[1..]);
|
||||||
PatStack::from_vec(new_fields)
|
PatStack::from_vec(new_fields)
|
||||||
}
|
}
|
||||||
|
@ -475,7 +475,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
|
||||||
let mut matrix = Matrix::empty();
|
let mut matrix = Matrix::empty();
|
||||||
for row in &self.patterns {
|
for row in &self.patterns {
|
||||||
if ctor.is_covered_by(pcx, row.head().ctor()) {
|
if ctor.is_covered_by(pcx, row.head().ctor()) {
|
||||||
let new_row = row.pop_head_constructor(pcx.cx, ctor);
|
let new_row = row.pop_head_constructor(pcx, ctor);
|
||||||
matrix.push(new_row);
|
matrix.push(new_row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -786,7 +786,7 @@ fn is_useful<'p, 'tcx>(
|
||||||
is_under_guard: bool,
|
is_under_guard: bool,
|
||||||
is_top_level: bool,
|
is_top_level: bool,
|
||||||
) -> Usefulness<'p, 'tcx> {
|
) -> Usefulness<'p, 'tcx> {
|
||||||
debug!("matrix,v={:?}{:?}", matrix, v);
|
debug!(?matrix, ?v);
|
||||||
let Matrix { patterns: rows, .. } = matrix;
|
let Matrix { patterns: rows, .. } = matrix;
|
||||||
|
|
||||||
// The base case. We are pattern-matching on () and the return value is
|
// The base case. We are pattern-matching on () and the return value is
|
||||||
|
@ -806,11 +806,6 @@ fn is_useful<'p, 'tcx>(
|
||||||
|
|
||||||
debug_assert!(rows.iter().all(|r| r.len() == v.len()));
|
debug_assert!(rows.iter().all(|r| r.len() == v.len()));
|
||||||
|
|
||||||
let ty = v.head().ty();
|
|
||||||
let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
|
|
||||||
debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span());
|
|
||||||
let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive };
|
|
||||||
|
|
||||||
// If the first pattern is an or-pattern, expand it.
|
// If the first pattern is an or-pattern, expand it.
|
||||||
let mut ret = Usefulness::new_not_useful(witness_preference);
|
let mut ret = Usefulness::new_not_useful(witness_preference);
|
||||||
if v.head().is_or_pat() {
|
if v.head().is_or_pat() {
|
||||||
|
@ -832,6 +827,19 @@ fn is_useful<'p, 'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let mut ty = v.head().ty();
|
||||||
|
|
||||||
|
// Opaque types can't get destructured/split, but the patterns can
|
||||||
|
// actually hint at hidden types, so we use the patterns' types instead.
|
||||||
|
if let ty::Opaque(..) = v.head().ty().kind() {
|
||||||
|
if let Some(row) = rows.first() {
|
||||||
|
ty = row.head().ty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
|
||||||
|
debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span());
|
||||||
|
let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive };
|
||||||
|
|
||||||
let v_ctor = v.head().ctor();
|
let v_ctor = v.head().ctor();
|
||||||
debug!(?v_ctor);
|
debug!(?v_ctor);
|
||||||
if let Constructor::IntRange(ctor_range) = &v_ctor {
|
if let Constructor::IntRange(ctor_range) = &v_ctor {
|
||||||
|
@ -853,7 +861,7 @@ fn is_useful<'p, 'tcx>(
|
||||||
debug!("specialize({:?})", ctor);
|
debug!("specialize({:?})", ctor);
|
||||||
// We cache the result of `Fields::wildcards` because it is used a lot.
|
// We cache the result of `Fields::wildcards` because it is used a lot.
|
||||||
let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor);
|
let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor);
|
||||||
let v = v.pop_head_constructor(cx, &ctor);
|
let v = v.pop_head_constructor(pcx, &ctor);
|
||||||
let usefulness = ensure_sufficient_stack(|| {
|
let usefulness = ensure_sufficient_stack(|| {
|
||||||
is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false)
|
is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false)
|
||||||
});
|
});
|
||||||
|
|
|
@ -48,6 +48,7 @@ impl<'tcx> Lift for PlaceElem<'tcx> {
|
||||||
match *self {
|
match *self {
|
||||||
ProjectionElem::Deref => ProjectionElem::Deref,
|
ProjectionElem::Deref => ProjectionElem::Deref,
|
||||||
ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty.lift()),
|
ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty.lift()),
|
||||||
|
ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty.lift()),
|
||||||
ProjectionElem::Index(ref i) => ProjectionElem::Index(i.lift()),
|
ProjectionElem::Index(ref i) => ProjectionElem::Index(i.lift()),
|
||||||
ProjectionElem::Subslice { from, to, from_end } => {
|
ProjectionElem::Subslice { from, to, from_end } => {
|
||||||
ProjectionElem::Subslice { from, to, from_end }
|
ProjectionElem::Subslice { from, to, from_end }
|
||||||
|
|
|
@ -28,6 +28,7 @@ fn is_stable(place: PlaceRef<'_>) -> bool {
|
||||||
ProjectionElem::Field { .. } |
|
ProjectionElem::Field { .. } |
|
||||||
ProjectionElem::ConstantIndex { .. } |
|
ProjectionElem::ConstantIndex { .. } |
|
||||||
ProjectionElem::Subslice { .. } |
|
ProjectionElem::Subslice { .. } |
|
||||||
|
ProjectionElem::OpaqueCast { .. } |
|
||||||
ProjectionElem::Downcast { .. } => true,
|
ProjectionElem::Downcast { .. } => true,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" }
|
||||||
rustc_index = { path = "../rustc_index" }
|
rustc_index = { path = "../rustc_index" }
|
||||||
rustc_session = { path = "../rustc_session" }
|
rustc_session = { path = "../rustc_session" }
|
||||||
rustc_target = { path = "../rustc_target" }
|
rustc_target = { path = "../rustc_target" }
|
||||||
|
rustc_macros = { path = "../rustc_macros" }
|
||||||
rustc_ast = { path = "../rustc_ast" }
|
rustc_ast = { path = "../rustc_ast" }
|
||||||
rustc_serialize = { path = "../rustc_serialize" }
|
rustc_serialize = { path = "../rustc_serialize" }
|
||||||
rustc_span = { path = "../rustc_span" }
|
rustc_span = { path = "../rustc_span" }
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
//! conflicts between multiple such attributes attached to the same
|
//! conflicts between multiple such attributes attached to the same
|
||||||
//! item.
|
//! item.
|
||||||
|
|
||||||
|
use crate::errors;
|
||||||
use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
|
use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
|
use rustc_errors::{fluent, pluralize, struct_span_err, Applicability, MultiSpan};
|
||||||
use rustc_expand::base::resolve_path;
|
use rustc_expand::base::resolve_path;
|
||||||
use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
|
use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
@ -175,16 +176,20 @@ impl CheckAttrVisitor<'_> {
|
||||||
if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
|
if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
|
||||||
attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
|
attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
|
||||||
{
|
{
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
match attr.style {
|
||||||
let msg = match attr.style {
|
ast::AttrStyle::Outer => self.tcx.emit_spanned_lint(
|
||||||
ast::AttrStyle::Outer => {
|
UNUSED_ATTRIBUTES,
|
||||||
"crate-level attribute should be an inner attribute: add an exclamation \
|
hir_id,
|
||||||
mark: `#![foo]`"
|
attr.span,
|
||||||
}
|
errors::OuterCrateLevelAttr,
|
||||||
ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
|
),
|
||||||
};
|
ast::AttrStyle::Inner => self.tcx.emit_spanned_lint(
|
||||||
lint.build(msg).emit();
|
UNUSED_ATTRIBUTES,
|
||||||
});
|
hir_id,
|
||||||
|
attr.span,
|
||||||
|
errors::InnerCrateLevelAttr,
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,37 +214,21 @@ impl CheckAttrVisitor<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
|
fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
lint.build(&format!(
|
UNUSED_ATTRIBUTES,
|
||||||
"`#[{sym}]` is ignored on struct fields, match arms and macro defs",
|
hir_id,
|
||||||
))
|
attr.span,
|
||||||
.warn(
|
errors::IgnoredAttrWithMacro { sym },
|
||||||
"this was previously accepted by the compiler but is \
|
);
|
||||||
being phased out; it will become a hard error in \
|
|
||||||
a future release!",
|
|
||||||
)
|
|
||||||
.note(
|
|
||||||
"see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
|
|
||||||
for more information",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
|
fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
lint.build(&format!("`#[{sym}]` is ignored on struct fields and match arms"))
|
UNUSED_ATTRIBUTES,
|
||||||
.warn(
|
hir_id,
|
||||||
"this was previously accepted by the compiler but is \
|
attr.span,
|
||||||
being phased out; it will become a hard error in \
|
errors::IgnoredAttr { sym },
|
||||||
a future release!",
|
);
|
||||||
)
|
|
||||||
.note(
|
|
||||||
"see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
|
|
||||||
for more information",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
|
/// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
|
||||||
|
@ -249,9 +238,12 @@ impl CheckAttrVisitor<'_> {
|
||||||
| Target::Closure
|
| Target::Closure
|
||||||
| Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
|
| Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
|
||||||
Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
|
Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
lint.build("`#[inline]` is ignored on function prototypes").emit();
|
UNUSED_ATTRIBUTES,
|
||||||
});
|
hir_id,
|
||||||
|
attr.span,
|
||||||
|
errors::IgnoredInlineAttrFnProto,
|
||||||
|
);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
// FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
|
// FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
|
||||||
|
@ -259,19 +251,12 @@ impl CheckAttrVisitor<'_> {
|
||||||
// accidentally, to to be compatible with crates depending on them, we can't throw an
|
// accidentally, to to be compatible with crates depending on them, we can't throw an
|
||||||
// error here.
|
// error here.
|
||||||
Target::AssocConst => {
|
Target::AssocConst => {
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
lint.build("`#[inline]` is ignored on constants")
|
UNUSED_ATTRIBUTES,
|
||||||
.warn(
|
hir_id,
|
||||||
"this was previously accepted by the compiler but is \
|
attr.span,
|
||||||
being phased out; it will become a hard error in \
|
errors::IgnoredInlineAttrConstants,
|
||||||
a future release!",
|
);
|
||||||
)
|
|
||||||
.note(
|
|
||||||
"see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
|
|
||||||
for more information",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
});
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
// FIXME(#80564): Same for fields, arms, and macro defs
|
// FIXME(#80564): Same for fields, arms, and macro defs
|
||||||
|
@ -280,14 +265,10 @@ impl CheckAttrVisitor<'_> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
struct_span_err!(
|
self.tcx.sess.emit_err(errors::InlineNotFnOrClosure {
|
||||||
self.tcx.sess,
|
attr_span: attr.span,
|
||||||
attr.span,
|
defn_span: span,
|
||||||
E0518,
|
});
|
||||||
"attribute should be applied to function or closure",
|
|
||||||
)
|
|
||||||
.span_label(span, "not a function or closure")
|
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -309,36 +290,40 @@ impl CheckAttrVisitor<'_> {
|
||||||
|
|
||||||
// function prototypes can't be covered
|
// function prototypes can't be covered
|
||||||
Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
|
Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
lint.build("`#[no_coverage]` is ignored on function prototypes").emit();
|
UNUSED_ATTRIBUTES,
|
||||||
});
|
hir_id,
|
||||||
|
attr.span,
|
||||||
|
errors::IgnoredNoCoverageFnProto,
|
||||||
|
);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => {
|
Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => {
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
lint.build("`#[no_coverage]` does not propagate into items and must be applied to the contained functions directly").emit();
|
UNUSED_ATTRIBUTES,
|
||||||
});
|
hir_id,
|
||||||
|
attr.span,
|
||||||
|
errors::IgnoredNoCoveragePropagate,
|
||||||
|
);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
Target::Expression | Target::Statement | Target::Arm => {
|
Target::Expression | Target::Statement | Target::Arm => {
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
lint.build("`#[no_coverage]` may only be applied to function definitions")
|
UNUSED_ATTRIBUTES,
|
||||||
.emit();
|
hir_id,
|
||||||
});
|
attr.span,
|
||||||
|
errors::IgnoredNoCoverageFnDefn,
|
||||||
|
);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
struct_span_err!(
|
self.tcx.sess.emit_err(errors::IgnoredNoCoverageNotCoverable {
|
||||||
self.tcx.sess,
|
attr_span: attr.span,
|
||||||
attr.span,
|
defn_span: span,
|
||||||
E0788,
|
});
|
||||||
"`#[no_coverage]` must be applied to coverable code",
|
|
||||||
)
|
|
||||||
.span_label(span, "not coverable code")
|
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -389,14 +374,10 @@ impl CheckAttrVisitor<'_> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
|
||||||
.sess
|
attr_span: attr.span,
|
||||||
.struct_span_err(
|
defn_span: span,
|
||||||
attr.span,
|
});
|
||||||
"attribute should be applied to a function definition",
|
|
||||||
)
|
|
||||||
.span_label(span, "not a function definition")
|
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,14 +389,10 @@ impl CheckAttrVisitor<'_> {
|
||||||
Target::Fn
|
Target::Fn
|
||||||
| Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
|
| Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
|
||||||
.sess
|
attr_span: attr.span,
|
||||||
.struct_span_err(
|
defn_span: span,
|
||||||
attr.span,
|
});
|
||||||
"attribute should be applied to a function definition",
|
|
||||||
)
|
|
||||||
.span_label(span, "not a function definition")
|
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -432,13 +409,7 @@ impl CheckAttrVisitor<'_> {
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match target {
|
match target {
|
||||||
_ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
|
_ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
|
||||||
struct_span_err!(
|
self.tcx.sess.emit_err(errors::NakedTrackedCaller { attr_span });
|
||||||
self.tcx.sess,
|
|
||||||
attr_span,
|
|
||||||
E0736,
|
|
||||||
"cannot use `#[track_caller]` with `#[naked]`",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
|
Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
|
||||||
|
@ -453,14 +424,9 @@ impl CheckAttrVisitor<'_> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
struct_span_err!(
|
self.tcx
|
||||||
self.tcx.sess,
|
.sess
|
||||||
attr_span,
|
.emit_err(errors::TrackedCallerWrongLocation { attr_span, defn_span: span });
|
||||||
E0739,
|
|
||||||
"attribute should be applied to function"
|
|
||||||
)
|
|
||||||
.span_label(span, "not a function")
|
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -485,14 +451,10 @@ impl CheckAttrVisitor<'_> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
struct_span_err!(
|
self.tcx.sess.emit_err(errors::NonExhaustiveWrongLocation {
|
||||||
self.tcx.sess,
|
attr_span: attr.span,
|
||||||
attr.span,
|
defn_span: span,
|
||||||
E0701,
|
});
|
||||||
"attribute can only be applied to a struct or enum"
|
|
||||||
)
|
|
||||||
.span_label(span, "not a struct or enum")
|
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -511,11 +473,10 @@ impl CheckAttrVisitor<'_> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
|
||||||
.sess
|
attr_span: attr.span,
|
||||||
.struct_span_err(attr.span, "attribute can only be applied to a trait")
|
defn_span: span,
|
||||||
.span_label(span, "not a trait")
|
});
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -531,11 +492,10 @@ impl CheckAttrVisitor<'_> {
|
||||||
match target {
|
match target {
|
||||||
Target::Trait => true,
|
Target::Trait => true,
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
|
||||||
.sess
|
attr_span: attr.span,
|
||||||
.struct_span_err(attr.span, "attribute can only be applied to a trait")
|
defn_span: span,
|
||||||
.span_label(span, "not a trait")
|
});
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -555,16 +515,12 @@ impl CheckAttrVisitor<'_> {
|
||||||
// FIXME: #[target_feature] was previously erroneously allowed on statements and some
|
// FIXME: #[target_feature] was previously erroneously allowed on statements and some
|
||||||
// crates used this, so only emit a warning.
|
// crates used this, so only emit a warning.
|
||||||
Target::Statement => {
|
Target::Statement => {
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
lint.build("attribute should be applied to a function")
|
UNUSED_ATTRIBUTES,
|
||||||
.warn(
|
hir_id,
|
||||||
"this was previously accepted by the compiler but is \
|
attr.span,
|
||||||
being phased out; it will become a hard error in \
|
errors::TargetFeatureOnStatement,
|
||||||
a future release!",
|
);
|
||||||
)
|
|
||||||
.span_label(span, "not a function")
|
|
||||||
.emit();
|
|
||||||
});
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
|
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
|
||||||
|
@ -576,11 +532,10 @@ impl CheckAttrVisitor<'_> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
|
||||||
.sess
|
attr_span: attr.span,
|
||||||
.struct_span_err(attr.span, "attribute should be applied to a function")
|
defn_span: span,
|
||||||
.span_label(span, "not a function")
|
});
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -591,24 +546,17 @@ impl CheckAttrVisitor<'_> {
|
||||||
match target {
|
match target {
|
||||||
Target::ForeignStatic | Target::Static => true,
|
Target::ForeignStatic | Target::Static => true,
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToStatic {
|
||||||
.sess
|
attr_span: attr.span,
|
||||||
.struct_span_err(attr.span, "attribute should be applied to a static")
|
defn_span: span,
|
||||||
.span_label(span, "not a static")
|
});
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) {
|
fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name });
|
||||||
.sess
|
|
||||||
.struct_span_err(
|
|
||||||
meta.span(),
|
|
||||||
&format!("doc {0} attribute expects a string: #[doc({0} = \"a\")]", attr_name),
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_doc_alias_value(
|
fn check_doc_alias_value(
|
||||||
|
@ -621,22 +569,12 @@ impl CheckAttrVisitor<'_> {
|
||||||
aliases: &mut FxHashMap<String, Span>,
|
aliases: &mut FxHashMap<String, Span>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let err_fn = move |span: Span, msg: &str| {
|
let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span());
|
||||||
tcx.sess.span_err(
|
let attr_str =
|
||||||
span,
|
&format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" });
|
||||||
&format!(
|
|
||||||
"`#[doc(alias{})]` {}",
|
|
||||||
if is_list { "(\"...\")" } else { " = \"...\"" },
|
|
||||||
msg,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
false
|
|
||||||
};
|
|
||||||
if doc_alias == kw::Empty {
|
if doc_alias == kw::Empty {
|
||||||
return err_fn(
|
tcx.sess.emit_err(errors::DocAliasEmpty { span, attr_str });
|
||||||
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
return false;
|
||||||
"attribute cannot have empty value",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let doc_alias_str = doc_alias.as_str();
|
let doc_alias_str = doc_alias.as_str();
|
||||||
|
@ -644,23 +582,16 @@ impl CheckAttrVisitor<'_> {
|
||||||
.chars()
|
.chars()
|
||||||
.find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
|
.find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
|
||||||
{
|
{
|
||||||
self.tcx.sess.span_err(
|
tcx.sess.emit_err(errors::DocAliasBadChar { span, attr_str, char_: c });
|
||||||
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
|
||||||
&format!(
|
|
||||||
"{:?} character isn't allowed in `#[doc(alias{})]`",
|
|
||||||
c,
|
|
||||||
if is_list { "(\"...\")" } else { " = \"...\"" },
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') {
|
if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') {
|
||||||
return err_fn(
|
tcx.sess.emit_err(errors::DocAliasStartEnd { span, attr_str });
|
||||||
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
return false;
|
||||||
"cannot start or end with ' '",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if let Some(err) = match target {
|
|
||||||
|
let span = meta.span();
|
||||||
|
if let Some(location) = match target {
|
||||||
Target::Impl => Some("implementation block"),
|
Target::Impl => Some("implementation block"),
|
||||||
Target::ForeignMod => Some("extern block"),
|
Target::ForeignMod => Some("extern block"),
|
||||||
Target::AssocTy => {
|
Target::AssocTy => {
|
||||||
|
@ -686,19 +617,21 @@ impl CheckAttrVisitor<'_> {
|
||||||
Target::Param => return false,
|
Target::Param => return false,
|
||||||
_ => None,
|
_ => None,
|
||||||
} {
|
} {
|
||||||
return err_fn(meta.span(), &format!("isn't allowed on {}", err));
|
tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location });
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
let item_name = self.tcx.hir().name(hir_id);
|
let item_name = self.tcx.hir().name(hir_id);
|
||||||
if item_name == doc_alias {
|
if item_name == doc_alias {
|
||||||
return err_fn(meta.span(), "is the same as the item's name");
|
tcx.sess.emit_err(errors::DocAliasNotAnAlias { span, attr_str });
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
let span = meta.span();
|
|
||||||
if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) {
|
if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) {
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, span, |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
lint.build("doc alias is duplicated")
|
UNUSED_ATTRIBUTES,
|
||||||
.span_label(*entry.entry.get(), "first defined here")
|
hir_id,
|
||||||
.emit();
|
span,
|
||||||
});
|
errors::DocAliasDuplicated { first_defn: *entry.entry.get() },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -723,22 +656,12 @@ impl CheckAttrVisitor<'_> {
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx
|
||||||
.sess
|
.sess
|
||||||
.struct_span_err(
|
.emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
|
||||||
v.span(),
|
|
||||||
"`#[doc(alias(\"a\"))]` expects string literals",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
errors += 1;
|
errors += 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
|
||||||
.sess
|
|
||||||
.struct_span_err(
|
|
||||||
v.span(),
|
|
||||||
"`#[doc(alias(\"a\"))]` expects string literals",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
errors += 1;
|
errors += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -747,14 +670,7 @@ impl CheckAttrVisitor<'_> {
|
||||||
} else if let Some(doc_alias) = meta.value_str() {
|
} else if let Some(doc_alias) = meta.value_str() {
|
||||||
self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases)
|
self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases)
|
||||||
} else {
|
} else {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::DocAliasMalformed { span: meta.span() });
|
||||||
.sess
|
|
||||||
.struct_span_err(
|
|
||||||
meta.span(),
|
|
||||||
"doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \
|
|
||||||
strings `#[doc(alias(\"a\", \"b\"))]`",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -771,35 +687,20 @@ impl CheckAttrVisitor<'_> {
|
||||||
}) {
|
}) {
|
||||||
Some(ItemKind::Mod(ref module)) => {
|
Some(ItemKind::Mod(ref module)) => {
|
||||||
if !module.item_ids.is_empty() {
|
if !module.item_ids.is_empty() {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::DocKeywordEmptyMod { span: meta.span() });
|
||||||
.sess
|
|
||||||
.struct_span_err(
|
|
||||||
meta.span(),
|
|
||||||
"`#[doc(keyword = \"...\")]` can only be used on empty modules",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::DocKeywordNotMod { span: meta.span() });
|
||||||
.sess
|
|
||||||
.struct_span_err(
|
|
||||||
meta.span(),
|
|
||||||
"`#[doc(keyword = \"...\")]` can only be used on modules",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !rustc_lexer::is_ident(doc_keyword.as_str()) {
|
if !rustc_lexer::is_ident(doc_keyword.as_str()) {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::DocKeywordInvalidIdent {
|
||||||
.sess
|
span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
||||||
.struct_span_err(
|
doc_keyword,
|
||||||
meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
});
|
||||||
&format!("`{doc_keyword}` is not a valid identifier"),
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -812,24 +713,12 @@ impl CheckAttrVisitor<'_> {
|
||||||
}) {
|
}) {
|
||||||
Some(ItemKind::Impl(ref i)) => {
|
Some(ItemKind::Impl(ref i)) => {
|
||||||
if !matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) {
|
if !matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::DocTupleVariadicNotFirst { span: meta.span() });
|
||||||
.sess
|
|
||||||
.struct_span_err(
|
|
||||||
meta.span(),
|
|
||||||
"`#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::DocKeywordOnlyImpl { span: meta.span() });
|
||||||
.sess
|
|
||||||
.struct_span_err(
|
|
||||||
meta.span(),
|
|
||||||
"`#[doc(keyword = \"...\")]` can only be used on impl blocks",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -858,13 +747,9 @@ impl CheckAttrVisitor<'_> {
|
||||||
if let Some((prev_inline, prev_span)) = *specified_inline {
|
if let Some((prev_inline, prev_span)) = *specified_inline {
|
||||||
if do_inline != prev_inline {
|
if do_inline != prev_inline {
|
||||||
let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
|
let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
|
||||||
spans.push_span_label(prev_span, "this attribute...");
|
spans.push_span_label(prev_span, fluent::passes::doc_inline_conflict_first);
|
||||||
spans.push_span_label(meta.span(), "...conflicts with this attribute");
|
spans.push_span_label(meta.span(), fluent::passes::doc_inline_conflict_second);
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::DocKeywordConflict { spans });
|
||||||
.sess
|
|
||||||
.struct_span_err(spans, "conflicting doc inlining attributes")
|
|
||||||
.help("remove one of the conflicting attributes")
|
|
||||||
.emit();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -873,23 +758,14 @@ impl CheckAttrVisitor<'_> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.tcx.struct_span_lint_hir(
|
self.tcx.emit_spanned_lint(
|
||||||
INVALID_DOC_ATTRIBUTES,
|
INVALID_DOC_ATTRIBUTES,
|
||||||
hir_id,
|
hir_id,
|
||||||
meta.span(),
|
meta.span(),
|
||||||
|lint| {
|
errors::DocInlineOnlyUse {
|
||||||
let mut err = lint.build(
|
attr_span: meta.span(),
|
||||||
"this attribute can only be applied to a `use` item",
|
item_span: (attr.style == AttrStyle::Outer)
|
||||||
);
|
.then(|| self.tcx.hir().span(hir_id)),
|
||||||
err.span_label(meta.span(), "only applicable on `use` items");
|
|
||||||
if attr.style == AttrStyle::Outer {
|
|
||||||
err.span_label(
|
|
||||||
self.tcx.hir().span(hir_id),
|
|
||||||
"not a `use` item",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information")
|
|
||||||
.emit();
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
false
|
false
|
||||||
|
@ -904,15 +780,7 @@ impl CheckAttrVisitor<'_> {
|
||||||
attr_name: &str,
|
attr_name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if CRATE_HIR_ID == hir_id {
|
if CRATE_HIR_ID == hir_id {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name });
|
||||||
.sess
|
|
||||||
.struct_span_err(
|
|
||||||
meta.span(),
|
|
||||||
&format!(
|
|
||||||
"`#![doc({attr_name} = \"...\")]` isn't allowed as a crate-level attribute",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -926,36 +794,25 @@ impl CheckAttrVisitor<'_> {
|
||||||
hir_id: HirId,
|
hir_id: HirId,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if hir_id != CRATE_HIR_ID {
|
if hir_id != CRATE_HIR_ID {
|
||||||
self.tcx.struct_span_lint_hir(
|
self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| {
|
||||||
INVALID_DOC_ATTRIBUTES,
|
let mut err = lint.build(fluent::passes::attr_crate_level);
|
||||||
hir_id,
|
if attr.style == AttrStyle::Outer
|
||||||
meta.span(),
|
&& self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID
|
||||||
|lint| {
|
{
|
||||||
let mut err = lint.build(
|
if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) {
|
||||||
"this attribute can only be applied at the crate level",
|
src.insert(1, '!');
|
||||||
);
|
err.span_suggestion_verbose(
|
||||||
if attr.style == AttrStyle::Outer && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID {
|
attr.span,
|
||||||
if let Ok(mut src) =
|
fluent::passes::suggestion,
|
||||||
self.tcx.sess.source_map().span_to_snippet(attr.span)
|
src,
|
||||||
{
|
Applicability::MaybeIncorrect,
|
||||||
src.insert(1, '!');
|
);
|
||||||
err.span_suggestion_verbose(
|
} else {
|
||||||
attr.span,
|
err.span_help(attr.span, fluent::passes::help);
|
||||||
"to apply to the crate, use an inner attribute",
|
|
||||||
src,
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
err.span_help(
|
|
||||||
attr.span,
|
|
||||||
"to apply to the crate, use an inner attribute",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information")
|
}
|
||||||
.emit();
|
err.note(fluent::passes::note).emit();
|
||||||
},
|
});
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -970,18 +827,14 @@ impl CheckAttrVisitor<'_> {
|
||||||
match i_meta.name_or_empty() {
|
match i_meta.name_or_empty() {
|
||||||
sym::attr | sym::no_crate_inject => {}
|
sym::attr | sym::no_crate_inject => {}
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx.struct_span_lint_hir(
|
self.tcx.emit_spanned_lint(
|
||||||
INVALID_DOC_ATTRIBUTES,
|
INVALID_DOC_ATTRIBUTES,
|
||||||
hir_id,
|
hir_id,
|
||||||
i_meta.span(),
|
i_meta.span(),
|
||||||
|lint| {
|
errors::DocTestUnknown {
|
||||||
lint.build(&format!(
|
path: rustc_ast_pretty::pprust::path_to_string(
|
||||||
"unknown `doc(test)` attribute `{}`",
|
&i_meta.meta_item().unwrap().path,
|
||||||
rustc_ast_pretty::pprust::path_to_string(
|
),
|
||||||
&i_meta.meta_item().unwrap().path
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.emit();
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
is_valid = false;
|
is_valid = false;
|
||||||
|
@ -989,9 +842,12 @@ impl CheckAttrVisitor<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
lint.build("`#[doc(test(...)]` takes a list of attributes").emit();
|
INVALID_DOC_ATTRIBUTES,
|
||||||
});
|
hir_id,
|
||||||
|
meta.span(),
|
||||||
|
errors::DocTestTakesList,
|
||||||
|
);
|
||||||
is_valid = false;
|
is_valid = false;
|
||||||
}
|
}
|
||||||
is_valid
|
is_valid
|
||||||
|
@ -1093,79 +949,66 @@ impl CheckAttrVisitor<'_> {
|
||||||
|
|
||||||
sym::primitive => {
|
sym::primitive => {
|
||||||
if !self.tcx.features().rustdoc_internals {
|
if !self.tcx.features().rustdoc_internals {
|
||||||
self.tcx.struct_span_lint_hir(
|
self.tcx.emit_spanned_lint(
|
||||||
INVALID_DOC_ATTRIBUTES,
|
INVALID_DOC_ATTRIBUTES,
|
||||||
hir_id,
|
hir_id,
|
||||||
i_meta.span,
|
i_meta.span,
|
||||||
|lint| {
|
errors::DocPrimitive,
|
||||||
let mut diag = lint.build(
|
|
||||||
"`doc(primitive)` should never have been stable",
|
|
||||||
);
|
|
||||||
diag.emit();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx.struct_span_lint_hir(
|
let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path);
|
||||||
INVALID_DOC_ATTRIBUTES,
|
if i_meta.has_name(sym::spotlight) {
|
||||||
hir_id,
|
self.tcx.emit_spanned_lint(
|
||||||
i_meta.span,
|
INVALID_DOC_ATTRIBUTES,
|
||||||
|lint| {
|
hir_id,
|
||||||
let mut diag = lint.build(&format!(
|
i_meta.span,
|
||||||
"unknown `doc` attribute `{}`",
|
errors::DocTestUnknownSpotlight {
|
||||||
rustc_ast_pretty::pprust::path_to_string(&i_meta.path),
|
path,
|
||||||
));
|
span: i_meta.span
|
||||||
if i_meta.has_name(sym::spotlight) {
|
|
||||||
diag.note(
|
|
||||||
"`doc(spotlight)` was renamed to `doc(notable_trait)`",
|
|
||||||
);
|
|
||||||
diag.span_suggestion_short(
|
|
||||||
i_meta.span,
|
|
||||||
"use `notable_trait` instead",
|
|
||||||
"notable_trait",
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
diag.note("`doc(spotlight)` is now a no-op");
|
|
||||||
}
|
}
|
||||||
if i_meta.has_name(sym::include) {
|
);
|
||||||
if let Some(value) = i_meta.value_str() {
|
} else if i_meta.has_name(sym::include) &&
|
||||||
// if there are multiple attributes, the suggestion would suggest deleting all of them, which is incorrect
|
let Some(value) = i_meta.value_str() {
|
||||||
let applicability = if list.len() == 1 {
|
let applicability = if list.len() == 1 {
|
||||||
Applicability::MachineApplicable
|
Applicability::MachineApplicable
|
||||||
} else {
|
} else {
|
||||||
Applicability::MaybeIncorrect
|
Applicability::MaybeIncorrect
|
||||||
};
|
};
|
||||||
let inner = if attr.style == AttrStyle::Inner {
|
// If there are multiple attributes, the suggestion would suggest
|
||||||
"!"
|
// deleting all of them, which is incorrect.
|
||||||
} else {
|
self.tcx.emit_spanned_lint(
|
||||||
""
|
INVALID_DOC_ATTRIBUTES,
|
||||||
};
|
hir_id,
|
||||||
diag.span_suggestion(
|
i_meta.span,
|
||||||
attr.meta().unwrap().span,
|
errors::DocTestUnknownInclude {
|
||||||
"use `doc = include_str!` instead",
|
path,
|
||||||
format!(
|
value: value.to_string(),
|
||||||
"#{inner}[doc = include_str!(\"{value}\")]",
|
inner: (attr.style == AttrStyle::Inner)
|
||||||
),
|
.then_some("!")
|
||||||
applicability,
|
.unwrap_or(""),
|
||||||
);
|
sugg: (attr.meta().unwrap().span, applicability),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
diag.emit();
|
);
|
||||||
},
|
} else {
|
||||||
);
|
self.tcx.emit_spanned_lint(
|
||||||
|
INVALID_DOC_ATTRIBUTES,
|
||||||
|
hir_id,
|
||||||
|
i_meta.span,
|
||||||
|
errors::DocTestUnknownAny { path }
|
||||||
|
);
|
||||||
|
}
|
||||||
is_valid = false;
|
is_valid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.tcx.struct_span_lint_hir(
|
self.tcx.emit_spanned_lint(
|
||||||
INVALID_DOC_ATTRIBUTES,
|
INVALID_DOC_ATTRIBUTES,
|
||||||
hir_id,
|
hir_id,
|
||||||
meta.span(),
|
meta.span(),
|
||||||
|lint| {
|
errors::DocInvalid,
|
||||||
lint.build("invalid `doc` attribute").emit();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
is_valid = false;
|
is_valid = false;
|
||||||
}
|
}
|
||||||
|
@ -1180,14 +1023,7 @@ impl CheckAttrVisitor<'_> {
|
||||||
match target {
|
match target {
|
||||||
Target::Struct | Target::Enum | Target::TyAlias => true,
|
Target::Struct | Target::Enum | Target::TyAlias => true,
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::PassByValue { attr_span: attr.span, span });
|
||||||
.sess
|
|
||||||
.struct_span_err(
|
|
||||||
attr.span,
|
|
||||||
"`pass_by_value` attribute should be applied to a struct, enum or type alias.",
|
|
||||||
)
|
|
||||||
.span_label(span, "is not a struct, enum or type alias")
|
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1197,14 +1033,7 @@ impl CheckAttrVisitor<'_> {
|
||||||
match target {
|
match target {
|
||||||
Target::Method(MethodKind::Inherent) => true,
|
Target::Method(MethodKind::Inherent) => true,
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span });
|
||||||
.sess
|
|
||||||
.struct_span_err(
|
|
||||||
attr.span,
|
|
||||||
"`rustc_allow_incoherent_impl` attribute should be applied to impl items.",
|
|
||||||
)
|
|
||||||
.span_label(span, "the only currently supported targets are inherent methods")
|
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1223,12 +1052,7 @@ impl CheckAttrVisitor<'_> {
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx
|
||||||
.sess
|
.sess
|
||||||
.struct_span_err(
|
.emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span });
|
||||||
attr.span,
|
|
||||||
"`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.",
|
|
||||||
)
|
|
||||||
.span_label(span, "only adts, extern types and traits are supported")
|
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1238,19 +1062,12 @@ impl CheckAttrVisitor<'_> {
|
||||||
fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
|
fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
|
||||||
let node = self.tcx.hir().get(hir_id);
|
let node = self.tcx.hir().get(hir_id);
|
||||||
if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() {
|
if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() {
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
lint.build(
|
UNUSED_ATTRIBUTES,
|
||||||
"`must_use` attribute on `async` functions \
|
hir_id,
|
||||||
applies to the anonymous `Future` returned by the \
|
attr.span,
|
||||||
function, not the value within",
|
errors::MustUseAsync { span }
|
||||||
)
|
);
|
||||||
.span_label(
|
|
||||||
span,
|
|
||||||
"this attribute does nothing, the `Future`s \
|
|
||||||
returned by async functions are already `must_use`",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !matches!(
|
if !matches!(
|
||||||
|
@ -1278,12 +1095,12 @@ impl CheckAttrVisitor<'_> {
|
||||||
_ => "a",
|
_ => "a",
|
||||||
};
|
};
|
||||||
|
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
lint.build(&format!(
|
UNUSED_ATTRIBUTES,
|
||||||
"`#[must_use]` has no effect when applied to {article} {target}"
|
hir_id,
|
||||||
))
|
attr.span,
|
||||||
.emit();
|
errors::MustUseNoEffect { article, target },
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now, its always valid
|
// For now, its always valid
|
||||||
|
@ -1295,11 +1112,7 @@ impl CheckAttrVisitor<'_> {
|
||||||
match target {
|
match target {
|
||||||
Target::Struct | Target::Enum | Target::Union | Target::Trait => true,
|
Target::Struct | Target::Enum | Target::Union | Target::Trait => true,
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx
|
self.tcx.sess.emit_err(errors::MustNotSuspend { attr_span: attr.span, span });
|
||||||
.sess
|
|
||||||
.struct_span_err(attr.span, "`must_not_suspend` attribute should be applied to a struct, enum, or trait")
|
|
||||||
.span_label(span, "is not a struct, enum, or trait")
|
|
||||||
.emit();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1319,16 +1132,12 @@ impl CheckAttrVisitor<'_> {
|
||||||
_ => {
|
_ => {
|
||||||
// FIXME: #[cold] was previously allowed on non-functions and some crates used
|
// FIXME: #[cold] was previously allowed on non-functions and some crates used
|
||||||
// this, so only emit a warning.
|
// this, so only emit a warning.
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
lint.build("attribute should be applied to a function")
|
UNUSED_ATTRIBUTES,
|
||||||
.warn(
|
hir_id,
|
||||||
"this was previously accepted by the compiler but is \
|
attr.span,
|
||||||
being phased out; it will become a hard error in \
|
errors::Cold { span },
|
||||||
a future release!",
|
);
|
||||||
)
|
|
||||||
.span_label(span, "not a function")
|
|
||||||
.emit();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1343,19 +1152,12 @@ impl CheckAttrVisitor<'_> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
|
self.tcx.emit_spanned_lint(
|
||||||
let mut diag =
|
UNUSED_ATTRIBUTES,
|
||||||
lint.build("attribute should be applied to an `extern` block with non-Rust ABI");
|
hir_id,
|
||||||
diag.warn(
|
attr.span,
|
||||||
"this was previously accepted by the compiler but is \
|
errors::Link { span: (target != Target::ForeignMod).then_some(span) },
|
||||||
being phased out; it will become a hard error in \
|
);
|
||||||
a future release!",
|
|
||||||
);
|
|
||||||
if target != Target::ForeignMod {
|
|
||||||
diag.span_label(span, "not an `extern` block");
|
|
||||||
}
|
|
||||||
diag.emit();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
|
/// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
|
||||||
|
|
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)]
|
#![allow(rustc::potential_query_instability)]
|
||||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||||
#![feature(iter_intersperse)]
|
#![feature(iter_intersperse)]
|
||||||
#![feature(let_else)]
|
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
#![feature(let_else)]
|
||||||
#![feature(map_try_insert)]
|
#![feature(map_try_insert)]
|
||||||
#![feature(min_specialization)]
|
#![feature(min_specialization)]
|
||||||
#![feature(try_blocks)]
|
#![feature(try_blocks)]
|
||||||
|
@ -27,6 +27,7 @@ pub mod dead;
|
||||||
mod debugger_visualizer;
|
mod debugger_visualizer;
|
||||||
mod diagnostic_items;
|
mod diagnostic_items;
|
||||||
pub mod entry;
|
pub mod entry;
|
||||||
|
mod errors;
|
||||||
pub mod hir_id_validator;
|
pub mod hir_id_validator;
|
||||||
pub mod hir_stats;
|
pub mod hir_stats;
|
||||||
mod lang_items;
|
mod lang_items;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
|
use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic};
|
||||||
use rustc_span::{Span, Symbol};
|
use rustc_span::{Span, Symbol};
|
||||||
|
|
||||||
#[derive(SessionDiagnostic)]
|
#[derive(SessionDiagnostic)]
|
||||||
|
@ -73,3 +73,19 @@ pub struct InPublicInterface<'a> {
|
||||||
#[label(privacy::visibility_label)]
|
#[label(privacy::visibility_label)]
|
||||||
pub vis_span: Span,
|
pub vis_span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[lint(privacy::from_private_dep_in_public_interface)]
|
||||||
|
pub struct FromPrivateDependencyInPublicInterface<'a> {
|
||||||
|
pub kind: &'a str,
|
||||||
|
pub descr: String,
|
||||||
|
pub krate: Symbol,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[lint(privacy::private_in_public_lint)]
|
||||||
|
pub struct PrivateInPublicLint<'a> {
|
||||||
|
pub vis_descr: &'static str,
|
||||||
|
pub kind: &'a str,
|
||||||
|
pub descr: String,
|
||||||
|
}
|
||||||
|
|
|
@ -38,8 +38,8 @@ use std::ops::ControlFlow;
|
||||||
use std::{cmp, fmt, mem};
|
use std::{cmp, fmt, mem};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
FieldIsPrivate, FieldIsPrivateLabel, InPublicInterface, InPublicInterfaceTraits, ItemIsPrivate,
|
FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface,
|
||||||
UnnamedItemIsPrivate,
|
InPublicInterfaceTraits, ItemIsPrivate, PrivateInPublicLint, UnnamedItemIsPrivate,
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1716,19 +1716,14 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
|
||||||
|
|
||||||
fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
|
fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
|
||||||
if self.leaks_private_dep(def_id) {
|
if self.leaks_private_dep(def_id) {
|
||||||
self.tcx.struct_span_lint_hir(
|
self.tcx.emit_spanned_lint(
|
||||||
lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES,
|
lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES,
|
||||||
self.tcx.hir().local_def_id_to_hir_id(self.item_def_id),
|
self.tcx.hir().local_def_id_to_hir_id(self.item_def_id),
|
||||||
self.tcx.def_span(self.item_def_id.to_def_id()),
|
self.tcx.def_span(self.item_def_id.to_def_id()),
|
||||||
|lint| {
|
FromPrivateDependencyInPublicInterface {
|
||||||
lint.build(&format!(
|
kind,
|
||||||
"{} `{}` from private dependency '{}' in public \
|
descr: descr.to_string(),
|
||||||
interface",
|
krate: self.tcx.crate_name(def_id.krate),
|
||||||
kind,
|
|
||||||
descr,
|
|
||||||
self.tcx.crate_name(def_id.krate)
|
|
||||||
))
|
|
||||||
.emit();
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1754,12 +1749,14 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let span = self.tcx.def_span(self.item_def_id.to_def_id());
|
let span = self.tcx.def_span(self.item_def_id.to_def_id());
|
||||||
|
let descr = descr.to_string();
|
||||||
if self.has_old_errors
|
if self.has_old_errors
|
||||||
|| self.in_assoc_ty
|
|| self.in_assoc_ty
|
||||||
|| self.tcx.resolutions(()).has_pub_restricted
|
|| self.tcx.resolutions(()).has_pub_restricted
|
||||||
{
|
{
|
||||||
let descr = descr.to_string();
|
let descr = descr.to_string();
|
||||||
let vis_span = self.tcx.def_span(def_id);
|
let vis_span =
|
||||||
|
self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id));
|
||||||
if kind == "trait" {
|
if kind == "trait" {
|
||||||
self.tcx.sess.emit_err(InPublicInterfaceTraits {
|
self.tcx.sess.emit_err(InPublicInterfaceTraits {
|
||||||
span,
|
span,
|
||||||
|
@ -1778,19 +1775,11 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let err_code = if kind == "trait" { "E0445" } else { "E0446" };
|
self.tcx.emit_spanned_lint(
|
||||||
self.tcx.struct_span_lint_hir(
|
|
||||||
lint::builtin::PRIVATE_IN_PUBLIC,
|
lint::builtin::PRIVATE_IN_PUBLIC,
|
||||||
hir_id,
|
hir_id,
|
||||||
span,
|
span,
|
||||||
|lint| {
|
PrivateInPublicLint { vis_descr, kind, descr },
|
||||||
lint.build(&format!(
|
|
||||||
"{} (error {})",
|
|
||||||
format!("{} {} `{}` in public interface", vis_descr, kind, descr),
|
|
||||||
err_code
|
|
||||||
))
|
|
||||||
.emit();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_data_structures::sync::Lock;
|
use rustc_data_structures::sync::Lock;
|
||||||
|
use rustc_span::Symbol;
|
||||||
use rustc_target::abi::{Align, Size};
|
use rustc_target::abi::{Align, Size};
|
||||||
use std::cmp::{self, Ordering};
|
use std::cmp::{self, Ordering};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct VariantInfo {
|
pub struct VariantInfo {
|
||||||
pub name: Option<String>,
|
pub name: Option<Symbol>,
|
||||||
pub kind: SizeKind,
|
pub kind: SizeKind,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
pub align: u64,
|
pub align: u64,
|
||||||
|
@ -20,7 +21,7 @@ pub enum SizeKind {
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct FieldInfo {
|
pub struct FieldInfo {
|
||||||
pub name: String,
|
pub name: Symbol,
|
||||||
pub offset: u64,
|
pub offset: u64,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
pub align: u64,
|
pub align: u64,
|
||||||
|
@ -119,7 +120,7 @@ impl CodeStats {
|
||||||
let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
|
let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
|
||||||
let indent = if !struct_like {
|
let indent = if !struct_like {
|
||||||
let name = match name.as_ref() {
|
let name = match name.as_ref() {
|
||||||
Some(name) => name.to_owned(),
|
Some(name) => name.to_string(),
|
||||||
None => i.to_string(),
|
None => i.to_string(),
|
||||||
};
|
};
|
||||||
println!(
|
println!(
|
||||||
|
|
|
@ -23,7 +23,7 @@ use rustc_hir::GenericParam;
|
||||||
use rustc_hir::Item;
|
use rustc_hir::Item;
|
||||||
use rustc_hir::Node;
|
use rustc_hir::Node;
|
||||||
use rustc_infer::infer::error_reporting::same_type_modulo_infer;
|
use rustc_infer::infer::error_reporting::same_type_modulo_infer;
|
||||||
use rustc_infer::traits::{AmbiguousSelection, TraitEngine};
|
use rustc_infer::traits::TraitEngine;
|
||||||
use rustc_middle::traits::select::OverflowError;
|
use rustc_middle::traits::select::OverflowError;
|
||||||
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
|
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
|
||||||
use rustc_middle::ty::error::ExpectedFound;
|
use rustc_middle::ty::error::ExpectedFound;
|
||||||
|
@ -1403,7 +1403,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
|
||||||
fn annotate_source_of_ambiguity(
|
fn annotate_source_of_ambiguity(
|
||||||
&self,
|
&self,
|
||||||
err: &mut Diagnostic,
|
err: &mut Diagnostic,
|
||||||
impls: &[AmbiguousSelection],
|
impls: &[DefId],
|
||||||
predicate: ty::Predicate<'tcx>,
|
predicate: ty::Predicate<'tcx>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2036,14 +2036,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
match selcx.select_from_obligation(&obligation) {
|
match selcx.select_from_obligation(&obligation) {
|
||||||
Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => {
|
Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => {
|
||||||
if self.is_tainted_by_errors() && subst.is_none() {
|
|
||||||
// If `subst.is_none()`, then this is probably two param-env
|
|
||||||
// candidates or impl candidates that are equal modulo lifetimes.
|
|
||||||
// Therefore, if we've already emitted an error, just skip this
|
|
||||||
// one, since it's not particularly actionable.
|
|
||||||
err.cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.annotate_source_of_ambiguity(&mut err, &impls, predicate);
|
self.annotate_source_of_ambiguity(&mut err, &impls, predicate);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -2224,35 +2216,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
fn annotate_source_of_ambiguity(
|
fn annotate_source_of_ambiguity(
|
||||||
&self,
|
&self,
|
||||||
err: &mut Diagnostic,
|
err: &mut Diagnostic,
|
||||||
impls: &[AmbiguousSelection],
|
impls: &[DefId],
|
||||||
predicate: ty::Predicate<'tcx>,
|
predicate: ty::Predicate<'tcx>,
|
||||||
) {
|
) {
|
||||||
let mut spans = vec![];
|
let mut spans = vec![];
|
||||||
let mut crates = vec![];
|
let mut crates = vec![];
|
||||||
let mut post = vec![];
|
let mut post = vec![];
|
||||||
let mut or_where_clause = false;
|
for def_id in impls {
|
||||||
for ambig in impls {
|
match self.tcx.span_of_impl(*def_id) {
|
||||||
match ambig {
|
Ok(span) => spans.push(span),
|
||||||
AmbiguousSelection::Impl(def_id) => match self.tcx.span_of_impl(*def_id) {
|
Err(name) => {
|
||||||
Ok(span) => spans.push(span),
|
crates.push(name);
|
||||||
Err(name) => {
|
if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) {
|
||||||
crates.push(name);
|
post.push(header);
|
||||||
if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) {
|
|
||||||
post.push(header);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
AmbiguousSelection::ParamEnv(span) => {
|
|
||||||
or_where_clause = true;
|
|
||||||
spans.push(*span);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let msg = format!(
|
let msg = format!("multiple `impl`s satisfying `{}` found", predicate);
|
||||||
"multiple `impl`s{} satisfying `{}` found",
|
|
||||||
if or_where_clause { " or `where` clauses" } else { "" },
|
|
||||||
predicate
|
|
||||||
);
|
|
||||||
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect();
|
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect();
|
||||||
crate_names.sort();
|
crate_names.sort();
|
||||||
crate_names.dedup();
|
crate_names.dedup();
|
||||||
|
|
|
@ -6,11 +6,9 @@
|
||||||
//!
|
//!
|
||||||
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
|
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
|
||||||
use hir::LangItem;
|
use hir::LangItem;
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_infer::traits::util::elaborate_predicates_with_span;
|
use rustc_infer::traits::TraitEngine;
|
||||||
use rustc_infer::traits::{AmbiguousSelection, TraitEngine};
|
|
||||||
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
|
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
|
||||||
use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT;
|
use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT;
|
||||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
|
@ -201,48 +199,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
// and report ambiguity.
|
// and report ambiguity.
|
||||||
if i > 1 {
|
if i > 1 {
|
||||||
debug!("multiple matches, ambig");
|
debug!("multiple matches, ambig");
|
||||||
|
|
||||||
// Collect a list of (probable) spans that point to a param-env candidate
|
|
||||||
let tcx = self.infcx.tcx;
|
|
||||||
let owner = stack.obligation.cause.body_id.owner.to_def_id();
|
|
||||||
let predicates = tcx.predicates_of(owner).instantiate_identity(tcx);
|
|
||||||
let param_env_spans: FxHashMap<_, _> = elaborate_predicates_with_span(
|
|
||||||
tcx,
|
|
||||||
std::iter::zip(predicates.predicates, predicates.spans),
|
|
||||||
)
|
|
||||||
.filter_map(|obligation| {
|
|
||||||
let kind = obligation.predicate.kind();
|
|
||||||
if let ty::PredicateKind::Trait(trait_pred) = kind.skip_binder() {
|
|
||||||
if trait_pred.trait_ref
|
|
||||||
== ty::TraitRef::identity(tcx, trait_pred.def_id())
|
|
||||||
.skip_binder()
|
|
||||||
{
|
|
||||||
// HACK: Remap the `Self: Trait` predicate that every trait has to a more useful span
|
|
||||||
Some((
|
|
||||||
kind.rebind(trait_pred),
|
|
||||||
tcx.def_span(trait_pred.def_id()),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Some((kind.rebind(trait_pred), obligation.cause.span))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
return Err(Ambiguous(
|
return Err(Ambiguous(
|
||||||
candidates
|
candidates
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|c| match c.candidate {
|
.filter_map(|c| match c.candidate {
|
||||||
SelectionCandidate::ImplCandidate(def_id) => {
|
SelectionCandidate::ImplCandidate(def_id) => Some(def_id),
|
||||||
Some(AmbiguousSelection::Impl(def_id))
|
|
||||||
}
|
|
||||||
SelectionCandidate::ParamCandidate(predicate) => {
|
|
||||||
Some(AmbiguousSelection::ParamEnv(
|
|
||||||
*param_env_spans.get(&predicate)?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|
|
@ -2486,7 +2486,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
concrete type's name `{type_name}` instead if you want to \
|
concrete type's name `{type_name}` instead if you want to \
|
||||||
specify its type parameters"
|
specify its type parameters"
|
||||||
),
|
),
|
||||||
type_name.to_string(),
|
type_name,
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -544,7 +544,7 @@ fn compare_self_type<'tcx>(
|
||||||
if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
|
if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
|
||||||
err.span_label(span, format!("trait method declared without `{self_descr}`"));
|
err.span_label(span, format!("trait method declared without `{self_descr}`"));
|
||||||
} else {
|
} else {
|
||||||
err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx));
|
err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
|
||||||
}
|
}
|
||||||
let reported = err.emit();
|
let reported = err.emit();
|
||||||
return Err(reported);
|
return Err(reported);
|
||||||
|
@ -564,7 +564,7 @@ fn compare_self_type<'tcx>(
|
||||||
if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
|
if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
|
||||||
err.span_label(span, format!("`{self_descr}` used in trait"));
|
err.span_label(span, format!("`{self_descr}` used in trait"));
|
||||||
} else {
|
} else {
|
||||||
err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx));
|
err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
|
||||||
}
|
}
|
||||||
let reported = err.emit();
|
let reported = err.emit();
|
||||||
return Err(reported);
|
return Err(reported);
|
||||||
|
@ -803,7 +803,7 @@ fn compare_number_of_method_arguments<'tcx>(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx));
|
err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
|
||||||
}
|
}
|
||||||
err.span_label(
|
err.span_label(
|
||||||
impl_span,
|
impl_span,
|
||||||
|
|
|
@ -1856,7 +1856,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let remaining_private_fields_len = remaining_private_fields.len();
|
let remaining_private_fields_len = remaining_private_fields.len();
|
||||||
let names = match &remaining_private_fields
|
let names = match &remaining_private_fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, _, _)| name.to_string())
|
.map(|(name, _, _)| name)
|
||||||
.collect::<Vec<_>>()[..]
|
.collect::<Vec<_>>()[..]
|
||||||
{
|
{
|
||||||
_ if remaining_private_fields_len > 6 => String::new(),
|
_ if remaining_private_fields_len > 6 => String::new(),
|
||||||
|
|
|
@ -19,6 +19,7 @@ use rustc_middle::ty::print::with_crate_prefix;
|
||||||
use rustc_middle::ty::ToPolyTraitRef;
|
use rustc_middle::ty::ToPolyTraitRef;
|
||||||
use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable};
|
use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable};
|
||||||
use rustc_span::symbol::{kw, sym, Ident};
|
use rustc_span::symbol::{kw, sym, Ident};
|
||||||
|
use rustc_span::Symbol;
|
||||||
use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span};
|
use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span};
|
||||||
use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _;
|
use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _;
|
||||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||||
|
@ -1548,7 +1549,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
Option<ObligationCause<'tcx>>,
|
Option<ObligationCause<'tcx>>,
|
||||||
)],
|
)],
|
||||||
) {
|
) {
|
||||||
let mut derives = Vec::<(String, Span, String)>::new();
|
let mut derives = Vec::<(String, Span, Symbol)>::new();
|
||||||
let mut traits = Vec::<Span>::new();
|
let mut traits = Vec::<Span>::new();
|
||||||
for (pred, _, _) in unsatisfied_predicates {
|
for (pred, _, _) in unsatisfied_predicates {
|
||||||
let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue };
|
let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue };
|
||||||
|
@ -1581,12 +1582,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
derives.push((
|
derives.push((
|
||||||
self_name.clone(),
|
self_name.clone(),
|
||||||
self_span,
|
self_span,
|
||||||
parent_diagnostic_name.to_string(),
|
parent_diagnostic_name,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
derives.push((self_name, self_span, diagnostic_name.to_string()));
|
derives.push((self_name, self_span, diagnostic_name));
|
||||||
} else {
|
} else {
|
||||||
traits.push(self.tcx.def_span(trait_pred.def_id()));
|
traits.push(self.tcx.def_span(trait_pred.def_id()));
|
||||||
}
|
}
|
||||||
|
@ -1609,7 +1610,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
derives_grouped.push((self_name, self_span, trait_name));
|
derives_grouped.push((self_name, self_span, trait_name.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = traits.len();
|
let len = traits.len();
|
||||||
|
|
|
@ -25,7 +25,7 @@ can be broken down into several distinct phases:
|
||||||
|
|
||||||
- regionck: after main is complete, the regionck pass goes over all
|
- regionck: after main is complete, the regionck pass goes over all
|
||||||
types looking for regions and making sure that they did not escape
|
types looking for regions and making sure that they did not escape
|
||||||
into places they are not in scope. This may also influence the
|
into places where they are not in scope. This may also influence the
|
||||||
final assignments of the various region variables if there is some
|
final assignments of the various region variables if there is some
|
||||||
flexibility.
|
flexibility.
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
use rustc_middle::ty::query::Providers;
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
|
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
|
||||||
use rustc_span::Span;
|
use rustc_span::{Span, Symbol};
|
||||||
|
|
||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
|
|
||||||
|
@ -123,12 +123,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
|
||||||
ty::GenericParamDefKind::Type { .. } => {
|
ty::GenericParamDefKind::Type { .. } => {
|
||||||
let param_ty = ty::ParamTy::for_def(param);
|
let param_ty = ty::ParamTy::for_def(param);
|
||||||
if !input_parameters.contains(&cgp::Parameter::from(param_ty)) {
|
if !input_parameters.contains(&cgp::Parameter::from(param_ty)) {
|
||||||
report_unused_parameter(
|
report_unused_parameter(tcx, tcx.def_span(param.def_id), "type", param_ty.name);
|
||||||
tcx,
|
|
||||||
tcx.def_span(param.def_id),
|
|
||||||
"type",
|
|
||||||
¶m_ty.to_string(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ty::GenericParamDefKind::Lifetime => {
|
ty::GenericParamDefKind::Lifetime => {
|
||||||
|
@ -140,7 +135,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
|
||||||
tcx,
|
tcx,
|
||||||
tcx.def_span(param.def_id),
|
tcx.def_span(param.def_id),
|
||||||
"lifetime",
|
"lifetime",
|
||||||
¶m.name.to_string(),
|
param.name,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,7 +146,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
|
||||||
tcx,
|
tcx,
|
||||||
tcx.def_span(param.def_id),
|
tcx.def_span(param.def_id),
|
||||||
"const",
|
"const",
|
||||||
¶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.
|
// used elsewhere are not projected back out.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) {
|
fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol) {
|
||||||
let mut err = struct_span_err!(
|
let mut err = struct_span_err!(
|
||||||
tcx.sess,
|
tcx.sess,
|
||||||
span,
|
span,
|
||||||
|
|
|
@ -836,14 +836,14 @@ impl<T: Clone, V: Borrow<[T]>> Join<&[T]> for [V] {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
impl<T> Borrow<[T]> for Vec<T> {
|
impl<T, A: Allocator> Borrow<[T]> for Vec<T, A> {
|
||||||
fn borrow(&self) -> &[T] {
|
fn borrow(&self) -> &[T] {
|
||||||
&self[..]
|
&self[..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
impl<T> BorrowMut<[T]> for Vec<T> {
|
impl<T, A: Allocator> BorrowMut<[T]> for Vec<T, A> {
|
||||||
fn borrow_mut(&mut self) -> &mut [T] {
|
fn borrow_mut(&mut self) -> &mut [T] {
|
||||||
&mut self[..]
|
&mut self[..]
|
||||||
}
|
}
|
||||||
|
|
|
@ -2120,8 +2120,9 @@ fn clean_use_statement<'tcx>(
|
||||||
// forcefully don't inline if this is not public or if the
|
// forcefully don't inline if this is not public or if the
|
||||||
// #[doc(no_inline)] attribute is present.
|
// #[doc(no_inline)] attribute is present.
|
||||||
// Don't inline doc(hidden) imports so they can be stripped at a later stage.
|
// Don't inline doc(hidden) imports so they can be stripped at a later stage.
|
||||||
let mut denied = !(visibility.is_public()
|
let mut denied = cx.output_format.is_json()
|
||||||
|| (cx.render_options.document_private && is_visible_from_parent_mod))
|
|| !(visibility.is_public()
|
||||||
|
|| (cx.render_options.document_private && is_visible_from_parent_mod))
|
||||||
|| pub_underscore
|
|| pub_underscore
|
||||||
|| attrs.iter().any(|a| {
|
|| attrs.iter().any(|a| {
|
||||||
a.has_name(sym::doc)
|
a.has_name(sym::doc)
|
||||||
|
|
|
@ -81,6 +81,8 @@ pub(crate) struct DocContext<'tcx> {
|
||||||
pub(crate) inlined: FxHashSet<ItemId>,
|
pub(crate) inlined: FxHashSet<ItemId>,
|
||||||
/// Used by `calculate_doc_coverage`.
|
/// Used by `calculate_doc_coverage`.
|
||||||
pub(crate) output_format: OutputFormat,
|
pub(crate) output_format: OutputFormat,
|
||||||
|
/// Used by `strip_private`.
|
||||||
|
pub(crate) show_coverage: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> DocContext<'tcx> {
|
impl<'tcx> DocContext<'tcx> {
|
||||||
|
@ -381,6 +383,7 @@ pub(crate) fn run_global_ctxt(
|
||||||
inlined: FxHashSet::default(),
|
inlined: FxHashSet::default(),
|
||||||
output_format,
|
output_format,
|
||||||
render_options,
|
render_options,
|
||||||
|
show_coverage,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Small hack to force the Sized trait to be present.
|
// Small hack to force the Sized trait to be present.
|
||||||
|
|
|
@ -43,7 +43,16 @@ impl JsonRenderer<'_> {
|
||||||
let span = item.span(self.tcx);
|
let span = item.span(self.tcx);
|
||||||
let clean::Item { name, attrs: _, kind: _, visibility, item_id, cfg: _ } = item;
|
let clean::Item { name, attrs: _, kind: _, visibility, item_id, cfg: _ } = item;
|
||||||
let inner = match *item.kind {
|
let inner = match *item.kind {
|
||||||
clean::StrippedItem(_) | clean::KeywordItem(_) => return None,
|
clean::KeywordItem(_) => return None,
|
||||||
|
clean::StrippedItem(ref inner) => {
|
||||||
|
match &**inner {
|
||||||
|
// We document non-empty stripped modules as with `Module::is_stripped` set to
|
||||||
|
// `true`, to prevent contained items from being orphaned for downstream users,
|
||||||
|
// as JSON does no inlining.
|
||||||
|
clean::ModuleItem(m) if !m.items.is_empty() => from_clean_item(item, self.tcx),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => from_clean_item(item, self.tcx),
|
_ => from_clean_item(item, self.tcx),
|
||||||
};
|
};
|
||||||
Some(Item {
|
Some(Item {
|
||||||
|
@ -220,7 +229,9 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
|
||||||
let header = item.fn_header(tcx);
|
let header = item.fn_header(tcx);
|
||||||
|
|
||||||
match *item.kind {
|
match *item.kind {
|
||||||
ModuleItem(m) => ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx) }),
|
ModuleItem(m) => {
|
||||||
|
ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx), is_stripped: false })
|
||||||
|
}
|
||||||
ImportItem(i) => ItemEnum::Import(i.into_tcx(tcx)),
|
ImportItem(i) => ItemEnum::Import(i.into_tcx(tcx)),
|
||||||
StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)),
|
StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)),
|
||||||
UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)),
|
UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)),
|
||||||
|
@ -257,8 +268,19 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
|
||||||
bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(),
|
bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(),
|
||||||
default: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
|
default: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
|
||||||
},
|
},
|
||||||
// `convert_item` early returns `None` for striped items and keywords.
|
// `convert_item` early returns `None` for stripped items and keywords.
|
||||||
StrippedItem(_) | KeywordItem(_) => unreachable!(),
|
KeywordItem(_) => unreachable!(),
|
||||||
|
StrippedItem(inner) => {
|
||||||
|
match *inner {
|
||||||
|
ModuleItem(m) => ItemEnum::Module(Module {
|
||||||
|
is_crate,
|
||||||
|
items: ids(m.items, tcx),
|
||||||
|
is_stripped: true,
|
||||||
|
}),
|
||||||
|
// `convert_item` early returns `None` for stripped items we're not including
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
ExternCrateItem { ref src } => ItemEnum::ExternCrate {
|
ExternCrateItem { ref src } => ItemEnum::ExternCrate {
|
||||||
name: name.as_ref().unwrap().to_string(),
|
name: name.as_ref().unwrap().to_string(),
|
||||||
rename: src.map(|x| x.to_string()),
|
rename: src.map(|x| x.to_string()),
|
||||||
|
|
|
@ -21,6 +21,7 @@ use rustc_span::def_id::LOCAL_CRATE;
|
||||||
use rustdoc_json_types as types;
|
use rustdoc_json_types as types;
|
||||||
|
|
||||||
use crate::clean::types::{ExternalCrate, ExternalLocation};
|
use crate::clean::types::{ExternalCrate, ExternalLocation};
|
||||||
|
use crate::clean::ItemKind;
|
||||||
use crate::config::RenderOptions;
|
use crate::config::RenderOptions;
|
||||||
use crate::docfs::PathError;
|
use crate::docfs::PathError;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
@ -175,6 +176,14 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
|
||||||
/// the hashmap because certain items (traits and types) need to have their mappings for trait
|
/// the hashmap because certain items (traits and types) need to have their mappings for trait
|
||||||
/// implementations filled out before they're inserted.
|
/// implementations filled out before they're inserted.
|
||||||
fn item(&mut self, item: clean::Item) -> Result<(), Error> {
|
fn item(&mut self, item: clean::Item) -> Result<(), Error> {
|
||||||
|
trace!("rendering {} {:?}", item.type_(), item.name);
|
||||||
|
|
||||||
|
// Flatten items that recursively store other items. We include orphaned items from
|
||||||
|
// stripped modules and etc that are otherwise reachable.
|
||||||
|
if let ItemKind::StrippedItem(inner) = &*item.kind {
|
||||||
|
inner.inner_items().for_each(|i| self.item(i.clone()).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
// Flatten items that recursively store other items
|
// Flatten items that recursively store other items
|
||||||
item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap());
|
item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap());
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) ->
|
||||||
retained: &mut retained,
|
retained: &mut retained,
|
||||||
access_levels: &cx.cache.access_levels,
|
access_levels: &cx.cache.access_levels,
|
||||||
update_retained: true,
|
update_retained: true,
|
||||||
|
is_json_output: cx.output_format.is_json() && !cx.show_coverage,
|
||||||
};
|
};
|
||||||
krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
|
krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use rustc_hir::def_id::DefId;
|
||||||
use rustc_middle::middle::privacy::AccessLevels;
|
use rustc_middle::middle::privacy::AccessLevels;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use crate::clean::{self, Item, ItemIdSet};
|
use crate::clean::{self, Item, ItemId, ItemIdSet};
|
||||||
use crate::fold::{strip_item, DocFolder};
|
use crate::fold::{strip_item, DocFolder};
|
||||||
use crate::formats::cache::Cache;
|
use crate::formats::cache::Cache;
|
||||||
|
|
||||||
|
@ -11,6 +11,21 @@ pub(crate) struct Stripper<'a> {
|
||||||
pub(crate) retained: &'a mut ItemIdSet,
|
pub(crate) retained: &'a mut ItemIdSet,
|
||||||
pub(crate) access_levels: &'a AccessLevels<DefId>,
|
pub(crate) access_levels: &'a AccessLevels<DefId>,
|
||||||
pub(crate) update_retained: bool,
|
pub(crate) update_retained: bool,
|
||||||
|
pub(crate) is_json_output: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Stripper<'a> {
|
||||||
|
// We need to handle this differently for the JSON output because some non exported items could
|
||||||
|
// be used in public API. And so, we need these items as well. `is_exported` only checks if they
|
||||||
|
// are in the public API, which is not enough.
|
||||||
|
#[inline]
|
||||||
|
fn is_item_reachable(&self, item_id: ItemId) -> bool {
|
||||||
|
if self.is_json_output {
|
||||||
|
self.access_levels.is_reachable(item_id.expect_def_id())
|
||||||
|
} else {
|
||||||
|
self.access_levels.is_exported(item_id.expect_def_id())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DocFolder for Stripper<'a> {
|
impl<'a> DocFolder for Stripper<'a> {
|
||||||
|
@ -45,9 +60,8 @@ impl<'a> DocFolder for Stripper<'a> {
|
||||||
| clean::TraitAliasItem(..)
|
| clean::TraitAliasItem(..)
|
||||||
| clean::MacroItem(..)
|
| clean::MacroItem(..)
|
||||||
| clean::ForeignTypeItem => {
|
| clean::ForeignTypeItem => {
|
||||||
if i.item_id.is_local()
|
let item_id = i.item_id;
|
||||||
&& !self.access_levels.is_exported(i.item_id.expect_def_id())
|
if item_id.is_local() && !self.is_item_reachable(item_id) {
|
||||||
{
|
|
||||||
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
|
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,6 +190,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||||
) -> bool {
|
) -> bool {
|
||||||
debug!("maybe_inline_local res: {:?}", res);
|
debug!("maybe_inline_local res: {:?}", res);
|
||||||
|
|
||||||
|
if self.cx.output_format.is_json() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let tcx = self.cx.tcx;
|
let tcx = self.cx.tcx;
|
||||||
let Some(res_did) = res.opt_def_id() else {
|
let Some(res_did) = res.opt_def_id() else {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -9,7 +9,7 @@ use std::path::PathBuf;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// rustdoc format-version.
|
/// rustdoc format-version.
|
||||||
pub const FORMAT_VERSION: u32 = 15;
|
pub const FORMAT_VERSION: u32 = 16;
|
||||||
|
|
||||||
/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
|
/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
|
||||||
/// about the language items in the local crate, as well as info about external items to allow
|
/// about the language items in the local crate, as well as info about external items to allow
|
||||||
|
@ -245,6 +245,9 @@ pub enum ItemEnum {
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
pub is_crate: bool,
|
pub is_crate: bool,
|
||||||
pub items: Vec<Id>,
|
pub items: Vec<Id>,
|
||||||
|
/// If `true`, this module is not part of the public API, but it contains
|
||||||
|
/// items that are re-exported as public API.
|
||||||
|
pub is_stripped: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
goto: file://|DOC_PATH|/test_docs/index.html
|
goto: file://|DOC_PATH|/test_docs/index.html
|
||||||
// First, we check that the search results are hidden when the Escape key is pressed.
|
// First, we check that the search results are hidden when the Escape key is pressed.
|
||||||
write: (".search-input", "test")
|
write: (".search-input", "test")
|
||||||
|
// To be SURE that the search will be run.
|
||||||
|
press-key: 'Enter'
|
||||||
wait-for: "#search h1" // The search element is empty before the first search
|
wait-for: "#search h1" // The search element is empty before the first search
|
||||||
// Check that the currently displayed element is search.
|
// Check that the currently displayed element is search.
|
||||||
wait-for: "#alternative-display #search"
|
wait-for: "#alternative-display #search"
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
goto: file://|DOC_PATH|/test_docs/index.html
|
goto: file://|DOC_PATH|/test_docs/index.html
|
||||||
show-text: true
|
show-text: true
|
||||||
write: (".search-input", "test")
|
write: (".search-input", "test")
|
||||||
|
// To be SURE that the search will be run.
|
||||||
|
press-key: 'Enter'
|
||||||
// Waiting for the search results to appear...
|
// Waiting for the search results to appear...
|
||||||
wait-for: "#titles"
|
wait-for: "#titles"
|
||||||
assert-text: ("#results .externcrate", "test_docs")
|
assert-text: ("#results .externcrate", "test_docs")
|
||||||
|
|
|
@ -7,6 +7,8 @@ reload:
|
||||||
assert-text: ("//*[@id='reexport.TheStdReexport']", "pub use ::std as TheStdReexport;")
|
assert-text: ("//*[@id='reexport.TheStdReexport']", "pub use ::std as TheStdReexport;")
|
||||||
assert-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"})
|
assert-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"})
|
||||||
write: (".search-input", "TheStdReexport")
|
write: (".search-input", "TheStdReexport")
|
||||||
|
// To be SURE that the search will be run.
|
||||||
|
press-key: 'Enter'
|
||||||
wait-for: "//a[@class='result-import']"
|
wait-for: "//a[@class='result-import']"
|
||||||
assert-attribute: (
|
assert-attribute: (
|
||||||
"//a[@class='result-import']",
|
"//a[@class='result-import']",
|
||||||
|
@ -18,6 +20,8 @@ click: "//a[@class='result-import']"
|
||||||
wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgb(73, 74, 61)"})
|
wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgb(73, 74, 61)"})
|
||||||
|
|
||||||
// We now check that the alias is working as well on the reexport.
|
// We now check that the alias is working as well on the reexport.
|
||||||
|
// To be SURE that the search will be run.
|
||||||
|
press-key: 'Enter'
|
||||||
write: (".search-input", "AliasForTheStdReexport")
|
write: (".search-input", "AliasForTheStdReexport")
|
||||||
wait-for: "//a[@class='result-import']"
|
wait-for: "//a[@class='result-import']"
|
||||||
assert-text: (
|
assert-text: (
|
||||||
|
|
|
@ -89,6 +89,8 @@ show-text: true
|
||||||
// We reload the page so the local storage settings are being used.
|
// We reload the page so the local storage settings are being used.
|
||||||
reload:
|
reload:
|
||||||
write: (".search-input", "thisisanalias")
|
write: (".search-input", "thisisanalias")
|
||||||
|
// To be SURE that the search will be run.
|
||||||
|
press-key: 'Enter'
|
||||||
// Waiting for the search results to appear...
|
// Waiting for the search results to appear...
|
||||||
wait-for: "#titles"
|
wait-for: "#titles"
|
||||||
// Checking that the colors for the alias element are the ones expected.
|
// Checking that the colors for the alias element are the ones expected.
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
goto: file://|DOC_PATH|/test_docs/index.html
|
goto: file://|DOC_PATH|/test_docs/index.html
|
||||||
size: (900, 1000)
|
size: (900, 1000)
|
||||||
write: (".search-input", "test")
|
write: (".search-input", "test")
|
||||||
|
// To be SURE that the search will be run.
|
||||||
|
press-key: 'Enter'
|
||||||
wait-for: "#search-settings"
|
wait-for: "#search-settings"
|
||||||
// The width is returned by "getComputedStyle" which returns the exact number instead of the
|
// The width is returned by "getComputedStyle" which returns the exact number instead of the
|
||||||
// CSS rule which is "50%"...
|
// CSS rule which is "50%"...
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// Checks that the "keyword" results have the expected text alongside them.
|
// Checks that the "keyword" results have the expected text alongside them.
|
||||||
goto: file://|DOC_PATH|/test_docs/index.html
|
goto: file://|DOC_PATH|/test_docs/index.html
|
||||||
write: (".search-input", "CookieMonster")
|
write: (".search-input", "CookieMonster")
|
||||||
|
// To be SURE that the search will be run.
|
||||||
|
press-key: 'Enter'
|
||||||
// Waiting for the search results to appear...
|
// Waiting for the search results to appear...
|
||||||
wait-for: "#titles"
|
wait-for: "#titles"
|
||||||
// Note: The two next assert commands could be merged as one but readability would be
|
// Note: The two next assert commands could be merged as one but readability would be
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// First, try a search-by-name
|
// First, try a search-by-name
|
||||||
goto: file://|DOC_PATH|/test_docs/index.html
|
goto: file://|DOC_PATH|/test_docs/index.html
|
||||||
write: (".search-input", "Foo")
|
write: (".search-input", "Foo")
|
||||||
|
// To be SURE that the search will be run.
|
||||||
|
press-key: 'Enter'
|
||||||
// Waiting for the search results to appear...
|
// Waiting for the search results to appear...
|
||||||
wait-for: "#titles"
|
wait-for: "#titles"
|
||||||
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||||
|
@ -22,6 +24,8 @@ wait-for-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"})
|
||||||
// Now try search-by-return
|
// Now try search-by-return
|
||||||
goto: file://|DOC_PATH|/test_docs/index.html
|
goto: file://|DOC_PATH|/test_docs/index.html
|
||||||
write: (".search-input", "-> String")
|
write: (".search-input", "-> String")
|
||||||
|
// To be SURE that the search will be run.
|
||||||
|
press-key: 'Enter'
|
||||||
// Waiting for the search results to appear...
|
// Waiting for the search results to appear...
|
||||||
wait-for: "#titles"
|
wait-for: "#titles"
|
||||||
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||||
|
@ -42,6 +46,8 @@ wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||||
// Try with a search-by-return with no results
|
// Try with a search-by-return with no results
|
||||||
goto: file://|DOC_PATH|/test_docs/index.html
|
goto: file://|DOC_PATH|/test_docs/index.html
|
||||||
write: (".search-input", "-> Something")
|
write: (".search-input", "-> Something")
|
||||||
|
// To be SURE that the search will be run.
|
||||||
|
press-key: 'Enter'
|
||||||
// Waiting for the search results to appear...
|
// Waiting for the search results to appear...
|
||||||
wait-for: "#titles"
|
wait-for: "#titles"
|
||||||
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||||
|
@ -50,6 +56,8 @@ assert-text: ("#titles > button:nth-of-type(1)", "In Function Return Types", STA
|
||||||
// Try with a search-by-parameter
|
// Try with a search-by-parameter
|
||||||
goto: file://|DOC_PATH|/test_docs/index.html
|
goto: file://|DOC_PATH|/test_docs/index.html
|
||||||
write: (".search-input", "usize pattern")
|
write: (".search-input", "usize pattern")
|
||||||
|
// To be SURE that the search will be run.
|
||||||
|
press-key: 'Enter'
|
||||||
// Waiting for the search results to appear...
|
// Waiting for the search results to appear...
|
||||||
wait-for: "#titles"
|
wait-for: "#titles"
|
||||||
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||||
|
@ -58,6 +66,8 @@ assert-text: ("#titles > button:nth-of-type(1)", "In Function Parameters", START
|
||||||
// Try with a search-by-parameter-and-return
|
// Try with a search-by-parameter-and-return
|
||||||
goto: file://|DOC_PATH|/test_docs/index.html
|
goto: file://|DOC_PATH|/test_docs/index.html
|
||||||
write: (".search-input", "pattern -> str")
|
write: (".search-input", "pattern -> str")
|
||||||
|
// To be SURE that the search will be run.
|
||||||
|
press-key: 'Enter'
|
||||||
// Waiting for the search results to appear...
|
// Waiting for the search results to appear...
|
||||||
wait-for: "#titles"
|
wait-for: "#titles"
|
||||||
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||||
|
|
|
@ -20,6 +20,8 @@ wait-for-css: ("#settings", {"display": "none"})
|
||||||
// Let's click on it when the search results are displayed.
|
// Let's click on it when the search results are displayed.
|
||||||
focus: ".search-input"
|
focus: ".search-input"
|
||||||
write: "test"
|
write: "test"
|
||||||
|
// To be SURE that the search will be run.
|
||||||
|
press-key: 'Enter'
|
||||||
wait-for: "#alternative-display #search"
|
wait-for: "#alternative-display #search"
|
||||||
click: "#settings-menu"
|
click: "#settings-menu"
|
||||||
wait-for-css: ("#settings", {"display": "block"})
|
wait-for-css: ("#settings", {"display": "block"})
|
||||||
|
|
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]
|
#![no_core]
|
||||||
#![feature(no_core)]
|
#![feature(no_core)]
|
||||||
|
|
||||||
// @!has glob_extern.json "$.index[*][?(@.name=='mod1')]"
|
// @is glob_extern.json "$.index[*][?(@.name=='mod1')].kind" \"module\"
|
||||||
|
// @is glob_extern.json "$.index[*][?(@.name=='mod1')].inner.is_stripped" "true"
|
||||||
mod mod1 {
|
mod mod1 {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// @set public_fn_id = - "$.index[*][?(@.name=='public_fn')].id"
|
// @has - "$.index[*][?(@.name=='public_fn')].id"
|
||||||
pub fn public_fn();
|
pub fn public_fn();
|
||||||
// @!has - "$.index[*][?(@.name=='private_fn')]"
|
// @!has - "$.index[*][?(@.name=='private_fn')]"
|
||||||
fn private_fn();
|
fn private_fn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @has - "$.index[*][?(@.name=='glob_extern')].inner.items[*]" $public_fn_id
|
// @is - "$.index[*][?(@.kind=='import')].inner.glob" true
|
||||||
pub use mod1::*;
|
pub use mod1::*;
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
#![no_core]
|
#![no_core]
|
||||||
#![feature(no_core)]
|
#![feature(no_core)]
|
||||||
|
|
||||||
// @!has glob_private.json "$.index[*][?(@.name=='mod1')]"
|
// @is glob_private.json "$.index[*][?(@.name=='mod1')].kind" \"module\"
|
||||||
|
// @is glob_private.json "$.index[*][?(@.name=='mod1')].inner.is_stripped" "true"
|
||||||
mod mod1 {
|
mod mod1 {
|
||||||
// @!has - "$.index[*][?(@.name=='mod2')]"
|
// @is - "$.index[*][?(@.name=='mod2')].kind" \"module\"
|
||||||
|
// @is - "$.index[*][?(@.name=='mod2')].inner.is_stripped" "true"
|
||||||
mod mod2 {
|
mod mod2 {
|
||||||
// @set m2pub_id = - "$.index[*][?(@.name=='Mod2Public')].id"
|
// @set m2pub_id = - "$.index[*][?(@.name=='Mod2Public')].id"
|
||||||
pub struct Mod2Public;
|
pub struct Mod2Public;
|
||||||
|
@ -13,15 +15,18 @@ mod mod1 {
|
||||||
// @!has - "$.index[*][?(@.name=='Mod2Private')]"
|
// @!has - "$.index[*][?(@.name=='Mod2Private')]"
|
||||||
struct Mod2Private;
|
struct Mod2Private;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='mod2')]"
|
||||||
pub use self::mod2::*;
|
pub use self::mod2::*;
|
||||||
|
|
||||||
// @set m1pub_id = - "$.index[*][?(@.name=='Mod1Public')].id"
|
// @set m1pub_id = - "$.index[*][?(@.name=='Mod1Public')].id"
|
||||||
pub struct Mod1Public;
|
pub struct Mod1Public;
|
||||||
|
|
||||||
// @!has - "$.index[*][?(@.name=='Mod1Private')]"
|
// @!has - "$.index[*][?(@.name=='Mod1Private')]"
|
||||||
struct Mod1Private;
|
struct Mod1Private;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='mod1')]"
|
||||||
pub use mod1::*;
|
pub use mod1::*;
|
||||||
|
|
||||||
// @has - "$.index[*][?(@.name=='glob_private')].inner.items[*]" $m2pub_id
|
// @has - "$.index[*][?(@.name=='mod2')].inner.items[*]" $m2pub_id
|
||||||
// @has - "$.index[*][?(@.name=='glob_private')].inner.items[*]" $m1pub_id
|
// @has - "$.index[*][?(@.name=='mod1')].inner.items[*]" $m1pub_id
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
#![feature(no_core)]
|
#![feature(no_core)]
|
||||||
#![no_core]
|
#![no_core]
|
||||||
|
|
||||||
|
// @is in_root_and_mod.json "$.index[*][?(@.name=='foo')].kind" \"module\"
|
||||||
|
// @is in_root_and_mod.json "$.index[*][?(@.name=='foo')].inner.is_stripped" "true"
|
||||||
mod foo {
|
mod foo {
|
||||||
// @set foo_id = in_root_and_mod.json "$.index[*][?(@.name=='Foo')].id"
|
// @has - "$.index[*][?(@.name=='Foo')]"
|
||||||
pub struct Foo;
|
pub struct Foo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @has - "$.index[*][?(@.name=='in_root_and_mod')].inner.items[*]" $foo_id
|
// @has - "$.index[*][?(@.kind=='import' && @.inner.source=='foo::Foo')]"
|
||||||
pub use foo::Foo;
|
pub use foo::Foo;
|
||||||
|
|
||||||
pub mod bar {
|
pub mod bar {
|
||||||
// @has - "$.index[*][?(@.name=='bar')].inner.items[*]" $foo_id
|
// @has - "$.index[*][?(@.kind=='import' && @.inner.source=='crate::foo::Foo')]"
|
||||||
pub use crate::foo::Foo;
|
pub use crate::foo::Foo;
|
||||||
}
|
}
|
||||||
|
|
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]
|
#![no_core]
|
||||||
#![feature(no_core)]
|
#![feature(no_core)]
|
||||||
// @!has rename_private.json "$.index[*][?(@.name=='inner')]"
|
|
||||||
|
// @is rename_private.json "$.index[*][?(@.name=='inner')].kind" \"module\"
|
||||||
|
// @is rename_private.json "$.index[*][?(@.name=='inner')].inner.is_stripped" "true"
|
||||||
mod inner {
|
mod inner {
|
||||||
// @!has - "$.index[*][?(@.name=='Public')]"
|
// @has - "$.index[*][?(@.name=='Public')]"
|
||||||
pub struct Public;
|
pub struct Public;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @set newname_id = - "$.index[*][?(@.name=='NewName')].id"
|
// @is - "$.index[*][?(@.kind=='import')].inner.name" \"NewName\"
|
||||||
// @is - "$.index[*][?(@.name=='NewName')].kind" \"struct\"
|
|
||||||
// @has - "$.index[*][?(@.name=='rename_private')].inner.items[*]" $newname_id
|
|
||||||
pub use inner::Public as NewName;
|
pub use inner::Public as NewName;
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
// Regression test for https://github.com/rust-lang/rust/issues/97432.
|
// Regression test for <https://github.com/rust-lang/rust/issues/97432>.
|
||||||
|
|
||||||
#![feature(no_core)]
|
#![feature(no_core)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_core]
|
#![no_core]
|
||||||
|
|
||||||
// @has same_type_reexported_more_than_once.json
|
// @has same_type_reexported_more_than_once.json
|
||||||
// @set trait_id = - "$.index[*][?(@.name=='Trait')].id"
|
// @has - "$.index[*][?(@.name=='Trait')]"
|
||||||
// @has - "$.index[*][?(@.name=='same_type_reexported_more_than_once')].inner.items[*]" $trait_id
|
|
||||||
pub use inner::Trait;
|
pub use inner::Trait;
|
||||||
// @set reexport_id = - "$.index[*][?(@.name=='Reexport')].id"
|
// @has - "$.index[*].inner[?(@.name=='Reexport')].id"
|
||||||
// @has - "$.index[*][?(@.name=='same_type_reexported_more_than_once')].inner.items[*]" $reexport_id
|
|
||||||
pub use inner::Trait as Reexport;
|
pub use inner::Trait as Reexport;
|
||||||
|
|
||||||
mod inner {
|
mod inner {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue