1
Fork 0

Auto merge of #122190 - matthiaskrgr:rollup-9ol4y30, r=matthiaskrgr

Rollup of 8 pull requests

Successful merges:

 - #121025 (add known-bug tests for derive failure to detect packed repr)
 - #121194 (Refactor pre-getopts command line argument handling)
 - #121563 (Use `ControlFlow` in visitors.)
 - #122173 (Don't ICE in CTFE if raw/fn-ptr types differ)
 - #122175 (Bless tidy issues order)
 - #122179 (rustc: Fix typo)
 - #122181 (Fix crash in internal late lint checking)
 - #122183 (interpret: update comment about read_discriminant on uninhabited variants)

Failed merges:

 - #122076 (Tweak the way we protect in-place function arguments in interpreters)
 - #122132 (Diagnostic renaming 3)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-03-08 17:31:00 +00:00
commit a655e648a9
76 changed files with 823 additions and 459 deletions

View file

@ -1,4 +1,5 @@
use super::LoweringContext;
use core::ops::ControlFlow;
use rustc_ast as ast;
use rustc_ast::visit::Visitor;
use rustc_ast::*;
@ -594,30 +595,32 @@ fn expand_format_args<'hir>(
}
fn may_contain_yield_point(e: &ast::Expr) -> bool {
struct MayContainYieldPoint(bool);
struct MayContainYieldPoint;
impl Visitor<'_> for MayContainYieldPoint {
fn visit_expr(&mut self, e: &ast::Expr) {
type Result = ControlFlow<()>;
fn visit_expr(&mut self, e: &ast::Expr) -> ControlFlow<()> {
if let ast::ExprKind::Await(_, _) | ast::ExprKind::Yield(_) = e.kind {
self.0 = true;
ControlFlow::Break(())
} else {
visit::walk_expr(self, e);
ControlFlow::Continue(())
}
}
fn visit_mac_call(&mut self, _: &ast::MacCall) {
fn visit_mac_call(&mut self, _: &ast::MacCall) -> ControlFlow<()> {
// Macros should be expanded at this point.
unreachable!("unexpanded macro in ast lowering");
}
fn visit_item(&mut self, _: &ast::Item) {
fn visit_item(&mut self, _: &ast::Item) -> ControlFlow<()> {
// Do not recurse into nested items.
ControlFlow::Continue(())
}
}
let mut visitor = MayContainYieldPoint(false);
visitor.visit_expr(e);
visitor.0
MayContainYieldPoint.visit_expr(e).is_break()
}
fn for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize)) {

View file

@ -1,6 +1,7 @@
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
use core::ops::ControlFlow;
use hir::ExprKind;
use rustc_errors::{Applicability, Diag};
use rustc_hir as hir;
@ -727,30 +728,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
_ => local_decl.source_info.span,
};
struct BindingFinder {
span: Span,
hir_id: Option<hir::HirId>,
}
impl<'tcx> Visitor<'tcx> for BindingFinder {
fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
if let hir::StmtKind::Local(local) = s.kind {
if local.pat.span == self.span {
self.hir_id = Some(local.hir_id);
}
}
hir::intravisit::walk_stmt(self, s);
}
}
let def_id = self.body.source.def_id();
let hir_id = if let Some(local_def_id) = def_id.as_local()
&& let Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id)
{
let body = self.infcx.tcx.hir().body(body_id);
let mut v = BindingFinder { span: pat_span, hir_id: None };
v.visit_body(body);
v.hir_id
BindingFinder { span: pat_span }.visit_body(body).break_value()
} else {
None
};
@ -859,17 +842,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
};
let hir_map = self.infcx.tcx.hir();
struct Finder<'tcx> {
struct Finder {
span: Span,
expr: Option<&'tcx Expr<'tcx>>,
}
impl<'tcx> Visitor<'tcx> for Finder<'tcx> {
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
if e.span == self.span && self.expr.is_none() {
self.expr = Some(e);
impl<'tcx> Visitor<'tcx> for Finder {
type Result = ControlFlow<&'tcx Expr<'tcx>>;
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) -> Self::Result {
if e.span == self.span {
ControlFlow::Break(e)
} else {
hir::intravisit::walk_expr(self, e)
}
hir::intravisit::walk_expr(self, e);
}
}
if let Some(body_id) = hir_map.maybe_body_owned_by(self.mir_def_id())
@ -878,9 +862,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
// `span` corresponds to the expression being iterated, find the `for`-loop desugared
// expression with that span in order to identify potential fixes when encountering a
// read-only iterator that should be mutable.
let mut v = Finder { span, expr: None };
v.visit_block(block);
if let Some(expr) = v.expr
if let ControlFlow::Break(expr) = (Finder { span }).visit_block(block)
&& let Call(_, [expr]) = expr.kind
{
match expr.kind {
@ -1179,29 +1161,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
);
}
Some((false, err_label_span, message)) => {
struct BindingFinder {
span: Span,
hir_id: Option<hir::HirId>,
}
impl<'tcx> Visitor<'tcx> for BindingFinder {
fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
if let hir::StmtKind::Local(local) = s.kind {
if local.pat.span == self.span {
self.hir_id = Some(local.hir_id);
}
}
hir::intravisit::walk_stmt(self, s);
}
}
let def_id = self.body.source.def_id();
let hir_id = if let Some(local_def_id) = def_id.as_local()
&& let Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id)
{
let body = self.infcx.tcx.hir().body(body_id);
let mut v = BindingFinder { span: err_label_span, hir_id: None };
v.visit_body(body);
v.hir_id
BindingFinder { span: err_label_span }.visit_body(body).break_value()
} else {
None
};
@ -1333,6 +1298,23 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
}
struct BindingFinder {
span: Span,
}
impl<'tcx> Visitor<'tcx> for BindingFinder {
type Result = ControlFlow<hir::HirId>;
fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) -> Self::Result {
if let hir::StmtKind::Local(local) = s.kind
&& local.pat.span == self.span
{
ControlFlow::Break(local.hir_id)
} else {
hir::intravisit::walk_stmt(self, s)
}
}
}
pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());

View file

@ -6,6 +6,7 @@
#![feature(assert_matches)]
#![feature(associated_type_bounds)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(let_chains)]
#![feature(min_specialization)]
#![feature(never_type)]

View file

@ -1,5 +1,6 @@
use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
use core::ops::ControlFlow;
use rustc_ast as ast;
use rustc_ast::mut_visit::MutVisitor;
use rustc_ast::ptr::P;
@ -87,41 +88,40 @@ fn flat_map_annotatable(
}
}
struct CfgFinder {
has_cfg_or_cfg_attr: bool,
}
fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool {
struct CfgFinder;
impl CfgFinder {
fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool {
let mut finder = CfgFinder { has_cfg_or_cfg_attr: false };
match annotatable {
Annotatable::Item(item) => finder.visit_item(item),
Annotatable::TraitItem(item) => finder.visit_assoc_item(item, visit::AssocCtxt::Trait),
Annotatable::ImplItem(item) => finder.visit_assoc_item(item, visit::AssocCtxt::Impl),
Annotatable::ForeignItem(item) => finder.visit_foreign_item(item),
Annotatable::Stmt(stmt) => finder.visit_stmt(stmt),
Annotatable::Expr(expr) => finder.visit_expr(expr),
Annotatable::Arm(arm) => finder.visit_arm(arm),
Annotatable::ExprField(field) => finder.visit_expr_field(field),
Annotatable::PatField(field) => finder.visit_pat_field(field),
Annotatable::GenericParam(param) => finder.visit_generic_param(param),
Annotatable::Param(param) => finder.visit_param(param),
Annotatable::FieldDef(field) => finder.visit_field_def(field),
Annotatable::Variant(variant) => finder.visit_variant(variant),
Annotatable::Crate(krate) => finder.visit_crate(krate),
};
finder.has_cfg_or_cfg_attr
}
}
impl<'ast> visit::Visitor<'ast> for CfgFinder {
fn visit_attribute(&mut self, attr: &'ast Attribute) {
// We want short-circuiting behavior, so don't use the '|=' operator.
self.has_cfg_or_cfg_attr = self.has_cfg_or_cfg_attr
|| attr
impl<'ast> visit::Visitor<'ast> for CfgFinder {
type Result = ControlFlow<()>;
fn visit_attribute(&mut self, attr: &'ast Attribute) -> ControlFlow<()> {
if attr
.ident()
.is_some_and(|ident| ident.name == sym::cfg || ident.name == sym::cfg_attr);
.is_some_and(|ident| ident.name == sym::cfg || ident.name == sym::cfg_attr)
{
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
}
let res = match annotatable {
Annotatable::Item(item) => CfgFinder.visit_item(item),
Annotatable::TraitItem(item) => CfgFinder.visit_assoc_item(item, visit::AssocCtxt::Trait),
Annotatable::ImplItem(item) => CfgFinder.visit_assoc_item(item, visit::AssocCtxt::Impl),
Annotatable::ForeignItem(item) => CfgFinder.visit_foreign_item(item),
Annotatable::Stmt(stmt) => CfgFinder.visit_stmt(stmt),
Annotatable::Expr(expr) => CfgFinder.visit_expr(expr),
Annotatable::Arm(arm) => CfgFinder.visit_arm(arm),
Annotatable::ExprField(field) => CfgFinder.visit_expr_field(field),
Annotatable::PatField(field) => CfgFinder.visit_pat_field(field),
Annotatable::GenericParam(param) => CfgFinder.visit_generic_param(param),
Annotatable::Param(param) => CfgFinder.visit_param(param),
Annotatable::FieldDef(field) => CfgFinder.visit_field_def(field),
Annotatable::Variant(variant) => CfgFinder.visit_variant(variant),
Annotatable::Crate(krate) => CfgFinder.visit_crate(krate),
};
res.is_break()
}
impl CfgEval<'_, '_> {
@ -132,7 +132,7 @@ impl CfgEval<'_, '_> {
fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Option<Annotatable> {
// Tokenizing and re-parsing the `Annotatable` can have a significant
// performance impact, so try to avoid it if possible
if !CfgFinder::has_cfg_or_cfg_attr(&annotatable) {
if !has_cfg_or_cfg_attr(&annotatable) {
return Some(annotatable);
}

View file

@ -1,6 +1,7 @@
use crate::deriving::generic::ty::*;
use crate::deriving::generic::*;
use crate::errors;
use core::ops::ControlFlow;
use rustc_ast as ast;
use rustc_ast::visit::walk_list;
use rustc_ast::{attr, EnumDef, VariantData};
@ -231,20 +232,19 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, '
}
fn has_a_default_variant(item: &Annotatable) -> bool {
struct HasDefaultAttrOnVariant {
found: bool,
}
struct HasDefaultAttrOnVariant;
impl<'ast> rustc_ast::visit::Visitor<'ast> for HasDefaultAttrOnVariant {
fn visit_variant(&mut self, v: &'ast rustc_ast::Variant) {
type Result = ControlFlow<()>;
fn visit_variant(&mut self, v: &'ast rustc_ast::Variant) -> ControlFlow<()> {
if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) {
self.found = true;
ControlFlow::Break(())
} else {
// no need to subrecurse.
ControlFlow::Continue(())
}
// no need to subrecurse.
}
}
let mut visitor = HasDefaultAttrOnVariant { found: false };
item.visit_with(&mut visitor);
visitor.found
item.visit_with(&mut HasDefaultAttrOnVariant).is_break()
}

View file

@ -339,7 +339,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
"ilp32s" | "lp64s" => e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT,
"ilp32f" | "lp64f" => e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT,
"ilp32d" | "lp64d" => e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT,
_ => bug!("unknown RISC-V ABI name"),
_ => bug!("unknown LoongArch ABI name"),
}
e_flags

View file

@ -10,6 +10,8 @@ use super::{ImmTy, InterpCx, InterpResult, Machine, Readable, Scalar, Writeable}
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Writes the discriminant of the given variant.
///
/// If the variant is uninhabited, this is UB.
#[instrument(skip(self), level = "trace")]
pub fn write_discriminant(
&mut self,
@ -102,6 +104,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Read discriminant, return the runtime value as well as the variant index.
/// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)!
///
/// Will never return an uninhabited variant.
#[instrument(skip(self), level = "trace")]
pub fn read_discriminant(
&self,
@ -244,7 +248,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
variant
}
};
// For consistency with `write_discriminant`, and to make sure that `project_downcast` cannot fail due to strange layouts, we declare immediate UB for uninhabited variants.
// Reading the discriminant of an uninhabited variant is UB. This is the basis for the
// `uninhabited_enum_branching` MIR pass. It also ensures consistency with
// `write_discriminant`.
if op.layout().for_variant(self, index).abi.is_uninhabited() {
throw_ub!(UninhabitedEnumVariantRead(index))
}

View file

@ -581,7 +581,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
if is_int_bool_or_char(lhs_ty) && is_int_bool_or_char(rhs_ty) {
// Int, bool, and char operations are fine.
} else if lhs_ty.is_fn_ptr() || lhs_ty.is_unsafe_ptr() {
assert_eq!(lhs_ty, rhs_ty);
assert!(matches!(
op,
BinOp::Eq

View file

@ -1,9 +1,7 @@
use std::error;
use std::fmt;
use std::fs;
use std::io;
use std::{env, error, fmt, fs, io};
use rustc_session::EarlyDiagCtxt;
use rustc_span::ErrorGuaranteed;
/// Expands argfiles in command line arguments.
#[derive(Default)]
@ -86,7 +84,7 @@ impl Expander {
fn read_file(path: &str) -> Result<String, Error> {
fs::read_to_string(path).map_err(|e| {
if e.kind() == io::ErrorKind::InvalidData {
Error::Utf8Error(Some(path.to_string()))
Error::Utf8Error(path.to_string())
} else {
Error::IOError(path.to_string(), e)
}
@ -94,23 +92,53 @@ impl Expander {
}
}
/// Replaces any `@file` arguments with the contents of `file`, with each line of `file` as a
/// separate argument.
///
/// **Note:** This function doesn't interpret argument 0 in any special way.
/// If this function is intended to be used with command line arguments,
/// `argv[0]` must be removed prior to calling it manually.
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec<String> {
pub fn arg_expand_all(
early_dcx: &EarlyDiagCtxt,
at_args: &[String],
) -> Result<Vec<String>, ErrorGuaranteed> {
let mut expander = Expander::default();
let mut result = Ok(());
for arg in at_args {
if let Err(err) = expander.arg(arg) {
early_dcx.early_fatal(format!("Failed to load argument file: {err}"));
result = Err(early_dcx.early_err(format!("failed to load argument file: {err}")));
}
}
expander.finish()
result.map(|()| expander.finish())
}
/// Gets the raw unprocessed command-line arguments as Unicode strings, without doing any further
/// processing (e.g., without `@file` expansion).
///
/// This function is identical to [`env::args()`] except that it emits an error when it encounters
/// non-Unicode arguments instead of panicking.
pub fn raw_args(early_dcx: &EarlyDiagCtxt) -> Result<Vec<String>, ErrorGuaranteed> {
let mut res = Ok(Vec::new());
for (i, arg) in env::args_os().enumerate() {
match arg.into_string() {
Ok(arg) => {
if let Ok(args) = &mut res {
args.push(arg);
}
}
Err(arg) => {
res =
Err(early_dcx.early_err(format!("argument {i} is not valid Unicode: {arg:?}")))
}
}
}
res
}
#[derive(Debug)]
pub enum Error {
Utf8Error(Option<String>),
enum Error {
Utf8Error(String),
IOError(String, io::Error),
ShellParseError(String),
}
@ -118,10 +146,9 @@ pub enum Error {
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Utf8Error(None) => write!(fmt, "Utf8 error"),
Error::Utf8Error(Some(path)) => write!(fmt, "Utf8 error in {path}"),
Error::IOError(path, err) => write!(fmt, "IO Error: {path}: {err}"),
Error::ShellParseError(path) => write!(fmt, "Invalid shell-style arguments in {path}"),
Error::Utf8Error(path) => write!(fmt, "UTF-8 error in {path}"),
Error::IOError(path, err) => write!(fmt, "IO error: {path}: {err}"),
Error::ShellParseError(path) => write!(fmt, "invalid shell-style arguments in {path}"),
}
}
}

View file

@ -292,7 +292,7 @@ fn run_compiler(
// the compiler with @empty_file as argv[0] and no more arguments.
let at_args = at_args.get(1..).unwrap_or_default();
let args = args::arg_expand_all(&default_early_dcx, at_args);
let args = args::arg_expand_all(&default_early_dcx, at_args)?;
let Some(matches) = handle_options(&default_early_dcx, &args) else { return Ok(()) };
@ -1515,15 +1515,7 @@ pub fn main() -> ! {
let mut callbacks = TimePassesCallbacks::default();
let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ());
let exit_code = catch_with_exit_code(|| {
let args = env::args_os()
.enumerate()
.map(|(i, arg)| {
arg.into_string().unwrap_or_else(|arg| {
early_dcx.early_fatal(format!("argument {i} is not valid Unicode: {arg:?}"))
})
})
.collect::<Vec<_>>();
RunCompiler::new(&args, &mut callbacks)
RunCompiler::new(&args::raw_args(&early_dcx)?, &mut callbacks)
.set_using_internal_features(using_internal_features)
.run()
});

View file

@ -1,5 +1,6 @@
use super::potentially_plural_count;
use crate::errors::{LifetimesOrBoundsMismatchOnTrait, MethodShouldReturnFuture};
use core::ops::ControlFlow;
use hir::def_id::{DefId, DefIdMap, LocalDefId};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, ErrorGuaranteed};
@ -1565,24 +1566,24 @@ fn compare_synthetic_generics<'tcx>(
let (sig, _) = impl_m.expect_fn();
let input_tys = sig.decl.inputs;
struct Visitor(Option<Span>, hir::def_id::LocalDefId);
struct Visitor(hir::def_id::LocalDefId);
impl<'v> intravisit::Visitor<'v> for Visitor {
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
intravisit::walk_ty(self, ty);
type Result = ControlFlow<Span>;
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) -> Self::Result {
if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind
&& let Res::Def(DefKind::TyParam, def_id) = path.res
&& def_id == self.1.to_def_id()
&& def_id == self.0.to_def_id()
{
self.0 = Some(ty.span);
ControlFlow::Break(ty.span)
} else {
intravisit::walk_ty(self, ty)
}
}
}
let mut visitor = Visitor(None, impl_def_id);
for ty in input_tys {
intravisit::Visitor::visit_ty(&mut visitor, ty);
}
let span = visitor.0?;
let span = input_tys.iter().find_map(|ty| {
intravisit::Visitor::visit_ty(&mut Visitor(impl_def_id), ty).break_value()
})?;
let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds;
let bounds = bounds.first()?.span().to(bounds.last()?.span());

View file

@ -6,6 +6,7 @@
//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices. This file
//! is also responsible for assigning their semantics to implicit lifetimes in trait objects.
use core::ops::ControlFlow;
use rustc_ast::visit::walk_list;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::{codes::*, struct_span_code_err};
@ -417,23 +418,18 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
{
if let &hir::ClosureBinder::For { span: for_sp, .. } = binder {
fn span_of_infer(ty: &hir::Ty<'_>) -> Option<Span> {
struct V(Option<Span>);
struct V;
impl<'v> Visitor<'v> for V {
fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
match t.kind {
_ if self.0.is_some() => (),
hir::TyKind::Infer => {
self.0 = Some(t.span);
}
_ => intravisit::walk_ty(self, t),
type Result = ControlFlow<Span>;
fn visit_ty(&mut self, t: &'v hir::Ty<'v>) -> Self::Result {
if matches!(t.kind, hir::TyKind::Infer) {
ControlFlow::Break(t.span)
} else {
intravisit::walk_ty(self, t)
}
}
}
let mut v = V(None);
v.visit_ty(ty);
v.0
V.visit_ty(ty).break_value()
}
let infer_in_rt_sp = match fn_decl.output {

View file

@ -1,3 +1,4 @@
use core::ops::ControlFlow;
use rustc_errors::{Applicability, StashKey};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
@ -675,19 +676,16 @@ pub fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
if tcx.features().lazy_type_alias {
return true;
}
struct HasTait {
has_type_alias_impl_trait: bool,
}
struct HasTait;
impl<'tcx> Visitor<'tcx> for HasTait {
fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
type Result = ControlFlow<()>;
fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) -> Self::Result {
if let hir::TyKind::OpaqueDef(..) = t.kind {
self.has_type_alias_impl_trait = true;
ControlFlow::Break(())
} else {
hir::intravisit::walk_ty(self, t);
hir::intravisit::walk_ty(self, t)
}
}
}
let mut has_tait = HasTait { has_type_alias_impl_trait: false };
has_tait.visit_ty(tcx.hir().expect_item(def_id).expect_ty_alias().0);
has_tait.has_type_alias_impl_trait
HasTait.visit_ty(tcx.hir().expect_item(def_id).expect_ty_alias().0).is_break()
}

View file

@ -6,6 +6,7 @@
use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
use crate::Expectation;
use crate::FnCtxt;
use core::ops::ControlFlow;
use rustc_ast::ast::Mutability;
use rustc_attr::parse_confusables;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
@ -2212,30 +2213,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let map = self.infcx.tcx.hir();
let body_id = self.tcx.hir().body_owned_by(self.body_id);
let body = map.body(body_id);
struct LetVisitor<'a> {
result: Option<&'a hir::Expr<'a>>,
struct LetVisitor {
ident_name: Symbol,
}
// FIXME: This really should be taking scoping, etc into account.
impl<'v> Visitor<'v> for LetVisitor<'v> {
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind
impl<'v> Visitor<'v> for LetVisitor {
type Result = ControlFlow<Option<&'v hir::Expr<'v>>>;
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
if let hir::StmtKind::Local(&hir::Local { pat, init, .. }) = ex.kind
&& let Binding(_, _, ident, ..) = pat.kind
&& ident.name == self.ident_name
{
self.result = *init;
ControlFlow::Break(init)
} else {
hir::intravisit::walk_stmt(self, ex);
hir::intravisit::walk_stmt(self, ex)
}
}
}
let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name };
visitor.visit_body(body);
if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id)
&& let Some(expr) = visitor.result
&& let ControlFlow::Break(Some(expr)) =
(LetVisitor { ident_name: seg1.ident.name }).visit_body(body)
&& let Some(self_ty) = self.node_ty_opt(expr.hir_id)
{
let probe = self.lookup_probe_for_diagnostic(

View file

@ -79,7 +79,7 @@ use rustc_middle::ty::{
use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
use rustc_target::spec::abi;
use std::borrow::Cow;
use std::ops::Deref;
use std::ops::{ControlFlow, Deref};
use std::path::PathBuf;
use std::{cmp, fmt, iter};
@ -2129,15 +2129,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let tykind = match self.tcx.opt_hir_node_by_def_id(trace.cause.body_id) {
Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) => {
let body = hir.body(*body_id);
struct LetVisitor<'v> {
struct LetVisitor {
span: Span,
result: Option<&'v hir::Ty<'v>>,
}
impl<'v> Visitor<'v> for LetVisitor<'v> {
fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
if self.result.is_some() {
return;
}
impl<'v> Visitor<'v> for LetVisitor {
type Result = ControlFlow<&'v hir::TyKind<'v>>;
fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) -> Self::Result {
// Find a local statement where the initializer has
// the same span as the error and the type is specified.
if let hir::Stmt {
@ -2151,13 +2148,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
} = s
&& init_span == &self.span
{
self.result = Some(*array_ty);
ControlFlow::Break(&array_ty.peel_refs().kind)
} else {
ControlFlow::Continue(())
}
}
}
let mut visitor = LetVisitor { span, result: None };
visitor.visit_body(body);
visitor.result.map(|r| &r.peel_refs().kind)
LetVisitor { span }.visit_body(body).break_value()
}
Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. })) => {
Some(&ty.peel_refs().kind)

View file

@ -1,3 +1,4 @@
use core::ops::ControlFlow;
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_middle::hir::map::Map;
@ -43,14 +44,9 @@ fn find_component_for_bound_region<'tcx>(
arg: &'tcx hir::Ty<'tcx>,
br: &ty::BoundRegionKind,
) -> Option<&'tcx hir::Ty<'tcx>> {
let mut nested_visitor = FindNestedTypeVisitor {
tcx,
bound_region: *br,
found_type: None,
current_index: ty::INNERMOST,
};
nested_visitor.visit_ty(arg);
nested_visitor.found_type
FindNestedTypeVisitor { tcx, bound_region: *br, current_index: ty::INNERMOST }
.visit_ty(arg)
.break_value()
}
// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
@ -65,26 +61,24 @@ struct FindNestedTypeVisitor<'tcx> {
// The bound_region corresponding to the Refree(freeregion)
// associated with the anonymous region we are looking for.
bound_region: ty::BoundRegionKind,
// The type where the anonymous lifetime appears
// for e.g., Vec<`&u8`> and <`&u8`>
found_type: Option<&'tcx hir::Ty<'tcx>>,
current_index: ty::DebruijnIndex,
}
impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
type Result = ControlFlow<&'tcx hir::Ty<'tcx>>;
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
self.tcx.hir()
}
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result {
match arg.kind {
hir::TyKind::BareFn(_) => {
self.current_index.shift_in(1);
intravisit::walk_ty(self, arg);
self.current_index.shift_out(1);
return;
return ControlFlow::Continue(());
}
hir::TyKind::TraitObject(bounds, ..) => {
@ -105,8 +99,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
(Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
if id == def_id {
self.found_type = Some(arg);
return; // we can stop visiting now
return ControlFlow::Break(arg);
}
}
@ -123,8 +116,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
);
debug!("LateBound id={:?} def_id={:?}", id, def_id);
if debruijn_index == self.current_index && id == def_id {
self.found_type = Some(arg);
return; // we can stop visiting now
return ControlFlow::Break(arg);
}
}
@ -145,23 +137,30 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
}
// Checks if it is of type `hir::TyKind::Path` which corresponds to a struct.
hir::TyKind::Path(_) => {
let subvisitor = &mut TyPathVisitor {
tcx: self.tcx,
found_it: false,
bound_region: self.bound_region,
current_index: self.current_index,
// Prefer using the lifetime in type arguments rather than lifetime arguments.
intravisit::walk_ty(self, arg)?;
// Call `walk_ty` as `visit_ty` is empty.
return if intravisit::walk_ty(
&mut TyPathVisitor {
tcx: self.tcx,
bound_region: self.bound_region,
current_index: self.current_index,
},
arg,
)
.is_break()
{
ControlFlow::Break(arg)
} else {
ControlFlow::Continue(())
};
intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty,
// this will visit only outermost type
if subvisitor.found_it {
self.found_type = Some(arg);
}
}
_ => {}
}
// walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
// go on to visit `&Foo`
intravisit::walk_ty(self, arg);
intravisit::walk_ty(self, arg)
}
}
@ -173,26 +172,25 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
// specific part of the type in the error message.
struct TyPathVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
found_it: bool,
bound_region: ty::BoundRegionKind,
current_index: ty::DebruijnIndex,
}
impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
type Result = ControlFlow<()>;
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Map<'tcx> {
self.tcx.hir()
}
fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) -> Self::Result {
match (self.tcx.named_bound_var(lifetime.hir_id), self.bound_region) {
// the lifetime of the TyPath!
(Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
if id == def_id {
self.found_it = true;
return; // we can stop visiting now
return ControlFlow::Break(());
}
}
@ -201,8 +199,7 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
debug!("id={:?}", id);
debug!("def_id={:?}", def_id);
if debruijn_index == self.current_index && id == def_id {
self.found_it = true;
return; // we can stop visiting now
return ControlFlow::Break(());
}
}
@ -220,9 +217,10 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
debug!("no arg found");
}
}
ControlFlow::Continue(())
}
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result {
// ignore nested types
//
// If you have a type like `Foo<'a, &Ty>` we
@ -231,5 +229,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
// Making `visit_ty` empty will ignore the `&Ty` embedded
// inside, it will get reached by the outer visitor.
debug!("`Ty` corresponding to a struct is {:?}", arg);
ControlFlow::Continue(())
}
}

View file

@ -1,4 +1,5 @@
use crate::infer::error_reporting::hir::Path;
use core::ops::ControlFlow;
use hir::def::CtorKind;
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
use hir::{Local, QPath};
@ -563,62 +564,55 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
cause: &ObligationCause<'_>,
span: Span,
) -> Option<TypeErrorAdditionalDiags> {
let hir = self.tcx.hir();
if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(cause.body_id) {
let body = hir.body(body_id);
/// Find the if expression with given span
struct IfVisitor {
pub found_if: bool,
pub err_span: Span,
}
/// Find the if expression with given span
struct IfVisitor {
pub result: bool,
pub found_if: bool,
pub err_span: Span,
}
impl<'v> Visitor<'v> for IfVisitor {
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
if self.result {
return;
impl<'v> Visitor<'v> for IfVisitor {
type Result = ControlFlow<()>;
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
match ex.kind {
hir::ExprKind::If(cond, _, _) => {
self.found_if = true;
walk_expr(self, cond)?;
self.found_if = false;
ControlFlow::Continue(())
}
match ex.kind {
hir::ExprKind::If(cond, _, _) => {
self.found_if = true;
walk_expr(self, cond);
self.found_if = false;
}
_ => walk_expr(self, ex),
}
}
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
if let hir::StmtKind::Local(hir::Local {
span,
pat: hir::Pat { .. },
ty: None,
init: Some(_),
..
}) = &ex.kind
&& self.found_if
&& span.eq(&self.err_span)
{
self.result = true;
}
walk_stmt(self, ex);
}
fn visit_body(&mut self, body: &'v hir::Body<'v>) {
hir::intravisit::walk_body(self, body);
_ => walk_expr(self, ex),
}
}
let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
visitor.visit_body(body);
if visitor.result {
return Some(TypeErrorAdditionalDiags::AddLetForLetChains {
span: span.shrink_to_lo(),
});
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
if let hir::StmtKind::Local(hir::Local {
span,
pat: hir::Pat { .. },
ty: None,
init: Some(_),
..
}) = &ex.kind
&& self.found_if
&& span.eq(&self.err_span)
{
ControlFlow::Break(())
} else {
walk_stmt(self, ex)
}
}
fn visit_body(&mut self, body: &'v hir::Body<'v>) -> Self::Result {
hir::intravisit::walk_body(self, body)
}
}
None
self.tcx.hir().maybe_body_owned_by(cause.body_id).and_then(|body_id| {
let body = self.tcx.hir().body(body_id);
IfVisitor { err_span: span, found_if: false }
.visit_body(body)
.is_break()
.then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() })
})
}
/// For "one type is more general than the other" errors on closures, suggest changing the lifetime

View file

@ -20,6 +20,7 @@
#![allow(rustc::untranslatable_diagnostic)]
#![feature(associated_type_bounds)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(extend_one)]
#![feature(let_chains)]
#![feature(if_let_guard)]

View file

@ -379,13 +379,15 @@ impl LateLintPass<'_> for Diagnostics {
_ => return, // occurs for fns passed as args
}
}
ExprKind::MethodCall(segment, _recv, args, _span) => {
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
let fn_gen_args = cx.typeck_results().node_args(expr.hir_id);
ExprKind::MethodCall(_segment, _recv, args, _span) => {
let Some((span, def_id, fn_gen_args)) = typeck_results_of_method_fn(cx, expr)
else {
return;
};
let mut call_tys: Vec<_> =
args.iter().map(|arg| cx.typeck_results().expr_ty(arg)).collect();
call_tys.insert(0, cx.tcx.types.self_param); // dummy inserted for `self`
(segment.ident.span, def_id, fn_gen_args, call_tys)
(span, def_id, fn_gen_args, call_tys)
}
_ => return,
};

View file

@ -20,6 +20,7 @@ use rustc_span::symbol::Symbol;
use rustc_span::symbol::{kw, sym};
use rustc_span::{BytePos, Span};
use std::iter;
use std::ops::ControlFlow;
declare_lint! {
/// The `unused_must_use` lint detects unused result of a type flagged as
@ -753,21 +754,18 @@ trait UnusedDelimLint {
// fn f(){(print!(á
// ```
use rustc_ast::visit::{walk_expr, Visitor};
struct ErrExprVisitor {
has_error: bool,
}
struct ErrExprVisitor;
impl<'ast> Visitor<'ast> for ErrExprVisitor {
fn visit_expr(&mut self, expr: &'ast ast::Expr) {
type Result = ControlFlow<()>;
fn visit_expr(&mut self, expr: &'ast ast::Expr) -> ControlFlow<()> {
if let ExprKind::Err(_) = expr.kind {
self.has_error = true;
return;
ControlFlow::Break(())
} else {
walk_expr(self, expr)
}
walk_expr(self, expr)
}
}
let mut visitor = ErrExprVisitor { has_error: false };
visitor.visit_expr(value);
if visitor.has_error {
if ErrExprVisitor.visit_expr(value).is_break() {
return;
}
let spans = match value.kind {

View file

@ -13,13 +13,14 @@ use ast::mut_visit::{noop_visit_expr, MutVisitor};
use ast::token::IdentIsRaw;
use ast::{CoroutineKind, ForLoopKind, GenBlockKind, Pat, Path, PathSegment};
use core::mem;
use core::ops::ControlFlow;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::Spacing;
use rustc_ast::util::case::Case;
use rustc_ast::util::classify;
use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
use rustc_ast::visit::Visitor;
use rustc_ast::visit::{walk_expr, Visitor};
use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, UnOp, DUMMY_NODE_ID};
use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
use rustc_ast::{Arm, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
@ -1703,19 +1704,20 @@ impl<'a> Parser<'a> {
let span = expr.span;
let found_labeled_breaks = {
struct FindLabeledBreaksVisitor(bool);
struct FindLabeledBreaksVisitor;
impl<'ast> Visitor<'ast> for FindLabeledBreaksVisitor {
fn visit_expr_post(&mut self, ex: &'ast Expr) {
type Result = ControlFlow<()>;
fn visit_expr(&mut self, ex: &'ast Expr) -> ControlFlow<()> {
if let ExprKind::Break(Some(_label), _) = ex.kind {
self.0 = true;
ControlFlow::Break(())
} else {
walk_expr(self, ex)
}
}
}
let mut vis = FindLabeledBreaksVisitor(false);
vis.visit_expr(&expr);
vis.0
FindLabeledBreaksVisitor.visit_expr(&expr).is_break()
};
// Suggestion involves adding a labeled block.

View file

@ -19,6 +19,7 @@ use crate::traits::{
ObligationCause, ObligationCauseCode, ObligationCtxt, Overflow, PredicateObligation,
SelectionError, SignatureMismatch, TraitNotObjectSafe,
};
use core::ops::ControlFlow;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_errors::codes::*;
use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart};
@ -1126,22 +1127,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
err: &mut Diag<'_>,
) -> bool {
let span = obligation.cause.span;
struct V<'v> {
struct V {
search_span: Span,
found: Option<&'v hir::Expr<'v>>,
}
impl<'v> Visitor<'v> for V<'v> {
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
impl<'v> Visitor<'v> for V {
type Result = ControlFlow<&'v hir::Expr<'v>>;
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
if let hir::ExprKind::Match(expr, _arms, hir::MatchSource::TryDesugar(_)) = ex.kind
&& ex.span.with_lo(ex.span.hi() - BytePos(1)).source_equal(self.search_span)
&& let hir::ExprKind::Call(_, [expr, ..]) = expr.kind
{
if ex.span.with_lo(ex.span.hi() - BytePos(1)).source_equal(self.search_span) {
if let hir::ExprKind::Call(_, [expr, ..]) = expr.kind {
self.found = Some(expr);
return;
}
}
ControlFlow::Break(expr)
} else {
hir::intravisit::walk_expr(self, ex)
}
hir::intravisit::walk_expr(self, ex);
}
}
let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id);
@ -1149,9 +1148,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => body_id,
_ => return false,
};
let mut v = V { search_span: span, found: None };
v.visit_body(self.tcx.hir().body(*body_id));
let Some(expr) = v.found else {
let ControlFlow::Break(expr) =
(V { search_span: span }).visit_body(self.tcx.hir().body(*body_id))
else {
return false;
};
let Some(typeck) = &self.typeck_results else {