Use ControlFlow
in HIR visitors
This commit is contained in:
parent
e760c44063
commit
822b10d428
@ -1,6 +1,7 @@
|
|||||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||||
#![allow(rustc::untranslatable_diagnostic)]
|
#![allow(rustc::untranslatable_diagnostic)]
|
||||||
|
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use hir::ExprKind;
|
use hir::ExprKind;
|
||||||
use rustc_errors::{Applicability, Diag};
|
use rustc_errors::{Applicability, Diag};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
@ -727,30 +728,12 @@ fn construct_mut_suggestion_for_local_binding_patterns(
|
|||||||
_ => local_decl.source_info.span,
|
_ => 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 def_id = self.body.source.def_id();
|
||||||
let hir_id = if let Some(local_def_id) = def_id.as_local()
|
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 Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id)
|
||||||
{
|
{
|
||||||
let body = self.infcx.tcx.hir().body(body_id);
|
let body = self.infcx.tcx.hir().body(body_id);
|
||||||
let mut v = BindingFinder { span: pat_span, hir_id: None };
|
BindingFinder { span: pat_span }.visit_body(body).break_value()
|
||||||
v.visit_body(body);
|
|
||||||
v.hir_id
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -859,17 +842,18 @@ fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diag<'_>, span: Span
|
|||||||
};
|
};
|
||||||
|
|
||||||
let hir_map = self.infcx.tcx.hir();
|
let hir_map = self.infcx.tcx.hir();
|
||||||
struct Finder<'tcx> {
|
struct Finder {
|
||||||
span: Span,
|
span: Span,
|
||||||
expr: Option<&'tcx Expr<'tcx>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Visitor<'tcx> for Finder<'tcx> {
|
impl<'tcx> Visitor<'tcx> for Finder {
|
||||||
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
|
type Result = ControlFlow<&'tcx Expr<'tcx>>;
|
||||||
if e.span == self.span && self.expr.is_none() {
|
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) -> Self::Result {
|
||||||
self.expr = Some(e);
|
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())
|
if let Some(body_id) = hir_map.maybe_body_owned_by(self.mir_def_id())
|
||||||
@ -878,9 +862,7 @@ fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
|
|||||||
// `span` corresponds to the expression being iterated, find the `for`-loop desugared
|
// `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
|
// expression with that span in order to identify potential fixes when encountering a
|
||||||
// read-only iterator that should be mutable.
|
// read-only iterator that should be mutable.
|
||||||
let mut v = Finder { span, expr: None };
|
if let ControlFlow::Break(expr) = (Finder { span }).visit_block(block)
|
||||||
v.visit_block(block);
|
|
||||||
if let Some(expr) = v.expr
|
|
||||||
&& let Call(_, [expr]) = expr.kind
|
&& let Call(_, [expr]) = expr.kind
|
||||||
{
|
{
|
||||||
match expr.kind {
|
match expr.kind {
|
||||||
@ -1179,29 +1161,12 @@ fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
Some((false, err_label_span, message)) => {
|
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 def_id = self.body.source.def_id();
|
||||||
let hir_id = if let Some(local_def_id) = def_id.as_local()
|
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 Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id)
|
||||||
{
|
{
|
||||||
let body = self.infcx.tcx.hir().body(body_id);
|
let body = self.infcx.tcx.hir().body(body_id);
|
||||||
let mut v = BindingFinder { span: err_label_span, hir_id: None };
|
BindingFinder { span: err_label_span }.visit_body(body).break_value()
|
||||||
v.visit_body(body);
|
|
||||||
v.hir_id
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -1333,6 +1298,23 @@ fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'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 {
|
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());
|
debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#![feature(assert_matches)]
|
#![feature(assert_matches)]
|
||||||
#![feature(associated_type_bounds)]
|
#![feature(associated_type_bounds)]
|
||||||
#![feature(box_patterns)]
|
#![feature(box_patterns)]
|
||||||
|
#![feature(control_flow_enum)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![feature(min_specialization)]
|
#![feature(min_specialization)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use super::potentially_plural_count;
|
use super::potentially_plural_count;
|
||||||
use crate::errors::{LifetimesOrBoundsMismatchOnTrait, MethodShouldReturnFuture};
|
use crate::errors::{LifetimesOrBoundsMismatchOnTrait, MethodShouldReturnFuture};
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use hir::def_id::{DefId, DefIdMap, LocalDefId};
|
use hir::def_id::{DefId, DefIdMap, LocalDefId};
|
||||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||||
use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, ErrorGuaranteed};
|
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 (sig, _) = impl_m.expect_fn();
|
||||||
let input_tys = sig.decl.inputs;
|
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 {
|
impl<'v> intravisit::Visitor<'v> for Visitor {
|
||||||
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
|
type Result = ControlFlow<Span>;
|
||||||
intravisit::walk_ty(self, ty);
|
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) -> Self::Result {
|
||||||
if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind
|
if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind
|
||||||
&& let Res::Def(DefKind::TyParam, def_id) = path.res
|
&& 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);
|
let span = input_tys.iter().find_map(|ty| {
|
||||||
for ty in input_tys {
|
intravisit::Visitor::visit_ty(&mut Visitor(impl_def_id), ty).break_value()
|
||||||
intravisit::Visitor::visit_ty(&mut visitor, ty);
|
})?;
|
||||||
}
|
|
||||||
let span = visitor.0?;
|
|
||||||
|
|
||||||
let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds;
|
let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds;
|
||||||
let bounds = bounds.first()?.span().to(bounds.last()?.span());
|
let bounds = bounds.first()?.span().to(bounds.last()?.span());
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices. This file
|
//! 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.
|
//! 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_ast::visit::walk_list;
|
||||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||||
use rustc_errors::{codes::*, struct_span_code_err};
|
use rustc_errors::{codes::*, struct_span_code_err};
|
||||||
@ -417,23 +418,18 @@ fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
|
|||||||
{
|
{
|
||||||
if let &hir::ClosureBinder::For { span: for_sp, .. } = binder {
|
if let &hir::ClosureBinder::For { span: for_sp, .. } = binder {
|
||||||
fn span_of_infer(ty: &hir::Ty<'_>) -> Option<Span> {
|
fn span_of_infer(ty: &hir::Ty<'_>) -> Option<Span> {
|
||||||
struct V(Option<Span>);
|
struct V;
|
||||||
|
|
||||||
impl<'v> Visitor<'v> for V {
|
impl<'v> Visitor<'v> for V {
|
||||||
fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
|
type Result = ControlFlow<Span>;
|
||||||
match t.kind {
|
fn visit_ty(&mut self, t: &'v hir::Ty<'v>) -> Self::Result {
|
||||||
_ if self.0.is_some() => (),
|
if matches!(t.kind, hir::TyKind::Infer) {
|
||||||
hir::TyKind::Infer => {
|
ControlFlow::Break(t.span)
|
||||||
self.0 = Some(t.span);
|
} else {
|
||||||
}
|
intravisit::walk_ty(self, t)
|
||||||
_ => intravisit::walk_ty(self, t),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
V.visit_ty(ty).break_value()
|
||||||
let mut v = V(None);
|
|
||||||
v.visit_ty(ty);
|
|
||||||
v.0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let infer_in_rt_sp = match fn_decl.output {
|
let infer_in_rt_sp = match fn_decl.output {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use core::ops::ControlFlow;
|
||||||
use rustc_errors::{Applicability, StashKey};
|
use rustc_errors::{Applicability, StashKey};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
@ -666,19 +667,16 @@ pub fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
|
|||||||
if tcx.features().lazy_type_alias {
|
if tcx.features().lazy_type_alias {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
struct HasTait {
|
struct HasTait;
|
||||||
has_type_alias_impl_trait: bool,
|
|
||||||
}
|
|
||||||
impl<'tcx> Visitor<'tcx> for 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 {
|
if let hir::TyKind::OpaqueDef(..) = t.kind {
|
||||||
self.has_type_alias_impl_trait = true;
|
ControlFlow::Break(())
|
||||||
} else {
|
} else {
|
||||||
hir::intravisit::walk_ty(self, t);
|
hir::intravisit::walk_ty(self, t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut has_tait = HasTait { has_type_alias_impl_trait: false };
|
HasTait.visit_ty(tcx.hir().expect_item(def_id).expect_ty_alias().0).is_break()
|
||||||
has_tait.visit_ty(tcx.hir().expect_item(def_id).expect_ty_alias().0);
|
|
||||||
has_tait.has_type_alias_impl_trait
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
|
use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
|
||||||
use crate::Expectation;
|
use crate::Expectation;
|
||||||
use crate::FnCtxt;
|
use crate::FnCtxt;
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use rustc_ast::ast::Mutability;
|
use rustc_ast::ast::Mutability;
|
||||||
use rustc_attr::parse_confusables;
|
use rustc_attr::parse_confusables;
|
||||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||||
@ -2212,30 +2213,28 @@ pub(crate) fn suggest_assoc_method_call(&self, segs: &[PathSegment<'_>]) {
|
|||||||
let map = self.infcx.tcx.hir();
|
let map = self.infcx.tcx.hir();
|
||||||
let body_id = self.tcx.hir().body_owned_by(self.body_id);
|
let body_id = self.tcx.hir().body_owned_by(self.body_id);
|
||||||
let body = map.body(body_id);
|
let body = map.body(body_id);
|
||||||
struct LetVisitor<'a> {
|
struct LetVisitor {
|
||||||
result: Option<&'a hir::Expr<'a>>,
|
|
||||||
ident_name: Symbol,
|
ident_name: Symbol,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: This really should be taking scoping, etc into account.
|
// FIXME: This really should be taking scoping, etc into account.
|
||||||
impl<'v> Visitor<'v> for LetVisitor<'v> {
|
impl<'v> Visitor<'v> for LetVisitor {
|
||||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
|
type Result = ControlFlow<Option<&'v hir::Expr<'v>>>;
|
||||||
if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind
|
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
|
&& let Binding(_, _, ident, ..) = pat.kind
|
||||||
&& ident.name == self.ident_name
|
&& ident.name == self.ident_name
|
||||||
{
|
{
|
||||||
self.result = *init;
|
ControlFlow::Break(init)
|
||||||
} else {
|
} 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)
|
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 Some(self_ty) = self.node_ty_opt(expr.hir_id)
|
||||||
{
|
{
|
||||||
let probe = self.lookup_probe_for_diagnostic(
|
let probe = self.lookup_probe_for_diagnostic(
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
|
use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
|
||||||
use rustc_target::spec::abi;
|
use rustc_target::spec::abi;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::ops::Deref;
|
use std::ops::{ControlFlow, Deref};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{cmp, fmt, iter};
|
use std::{cmp, fmt, iter};
|
||||||
|
|
||||||
@ -2129,15 +2129,12 @@ fn suggest_specify_actual_length(
|
|||||||
let tykind = match self.tcx.opt_hir_node_by_def_id(trace.cause.body_id) {
|
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), .. })) => {
|
Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) => {
|
||||||
let body = hir.body(*body_id);
|
let body = hir.body(*body_id);
|
||||||
struct LetVisitor<'v> {
|
struct LetVisitor {
|
||||||
span: Span,
|
span: Span,
|
||||||
result: Option<&'v hir::Ty<'v>>,
|
|
||||||
}
|
}
|
||||||
impl<'v> Visitor<'v> for LetVisitor<'v> {
|
impl<'v> Visitor<'v> for LetVisitor {
|
||||||
fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
|
type Result = ControlFlow<&'v hir::TyKind<'v>>;
|
||||||
if self.result.is_some() {
|
fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) -> Self::Result {
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Find a local statement where the initializer has
|
// Find a local statement where the initializer has
|
||||||
// the same span as the error and the type is specified.
|
// the same span as the error and the type is specified.
|
||||||
if let hir::Stmt {
|
if let hir::Stmt {
|
||||||
@ -2151,13 +2148,13 @@ fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
|
|||||||
} = s
|
} = s
|
||||||
&& init_span == &self.span
|
&& 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 };
|
LetVisitor { span }.visit_body(body).break_value()
|
||||||
visitor.visit_body(body);
|
|
||||||
visitor.result.map(|r| &r.peel_refs().kind)
|
|
||||||
}
|
}
|
||||||
Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. })) => {
|
Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. })) => {
|
||||||
Some(&ty.peel_refs().kind)
|
Some(&ty.peel_refs().kind)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use core::ops::ControlFlow;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::intravisit::{self, Visitor};
|
use rustc_hir::intravisit::{self, Visitor};
|
||||||
use rustc_middle::hir::map::Map;
|
use rustc_middle::hir::map::Map;
|
||||||
@ -43,14 +44,9 @@ fn find_component_for_bound_region<'tcx>(
|
|||||||
arg: &'tcx hir::Ty<'tcx>,
|
arg: &'tcx hir::Ty<'tcx>,
|
||||||
br: &ty::BoundRegionKind,
|
br: &ty::BoundRegionKind,
|
||||||
) -> Option<&'tcx hir::Ty<'tcx>> {
|
) -> Option<&'tcx hir::Ty<'tcx>> {
|
||||||
let mut nested_visitor = FindNestedTypeVisitor {
|
FindNestedTypeVisitor { tcx, bound_region: *br, current_index: ty::INNERMOST }
|
||||||
tcx,
|
.visit_ty(arg)
|
||||||
bound_region: *br,
|
.break_value()
|
||||||
found_type: None,
|
|
||||||
current_index: ty::INNERMOST,
|
|
||||||
};
|
|
||||||
nested_visitor.visit_ty(arg);
|
|
||||||
nested_visitor.found_type
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
|
// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
|
||||||
@ -65,26 +61,24 @@ struct FindNestedTypeVisitor<'tcx> {
|
|||||||
// The bound_region corresponding to the Refree(freeregion)
|
// The bound_region corresponding to the Refree(freeregion)
|
||||||
// associated with the anonymous region we are looking for.
|
// associated with the anonymous region we are looking for.
|
||||||
bound_region: ty::BoundRegionKind,
|
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,
|
current_index: ty::DebruijnIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
|
impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
|
||||||
|
type Result = ControlFlow<&'tcx hir::Ty<'tcx>>;
|
||||||
type NestedFilter = nested_filter::OnlyBodies;
|
type NestedFilter = nested_filter::OnlyBodies;
|
||||||
|
|
||||||
fn nested_visit_map(&mut self) -> Self::Map {
|
fn nested_visit_map(&mut self) -> Self::Map {
|
||||||
self.tcx.hir()
|
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 {
|
match arg.kind {
|
||||||
hir::TyKind::BareFn(_) => {
|
hir::TyKind::BareFn(_) => {
|
||||||
self.current_index.shift_in(1);
|
self.current_index.shift_in(1);
|
||||||
intravisit::walk_ty(self, arg);
|
intravisit::walk_ty(self, arg);
|
||||||
self.current_index.shift_out(1);
|
self.current_index.shift_out(1);
|
||||||
return;
|
return ControlFlow::Continue(());
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::TyKind::TraitObject(bounds, ..) => {
|
hir::TyKind::TraitObject(bounds, ..) => {
|
||||||
@ -105,8 +99,7 @@ fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
|
|||||||
(Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
|
(Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
|
||||||
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
|
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
|
||||||
if id == def_id {
|
if id == def_id {
|
||||||
self.found_type = Some(arg);
|
return ControlFlow::Break(arg);
|
||||||
return; // we can stop visiting now
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,8 +116,7 @@ fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
|
|||||||
);
|
);
|
||||||
debug!("LateBound id={:?} def_id={:?}", id, def_id);
|
debug!("LateBound id={:?} def_id={:?}", id, def_id);
|
||||||
if debruijn_index == self.current_index && id == def_id {
|
if debruijn_index == self.current_index && id == def_id {
|
||||||
self.found_type = Some(arg);
|
return ControlFlow::Break(arg);
|
||||||
return; // we can stop visiting now
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,23 +137,30 @@ fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
|
|||||||
}
|
}
|
||||||
// Checks if it is of type `hir::TyKind::Path` which corresponds to a struct.
|
// Checks if it is of type `hir::TyKind::Path` which corresponds to a struct.
|
||||||
hir::TyKind::Path(_) => {
|
hir::TyKind::Path(_) => {
|
||||||
let subvisitor = &mut TyPathVisitor {
|
// Prefer using the lifetime in type arguments rather than lifetime arguments.
|
||||||
tcx: self.tcx,
|
intravisit::walk_ty(self, arg)?;
|
||||||
found_it: false,
|
|
||||||
bound_region: self.bound_region,
|
// Call `walk_ty` as `visit_ty` is empty.
|
||||||
current_index: self.current_index,
|
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>`,
|
// walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
|
||||||
// go on to visit `&Foo`
|
// go on to visit `&Foo`
|
||||||
intravisit::walk_ty(self, arg);
|
intravisit::walk_ty(self, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,26 +172,25 @@ fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
|
|||||||
// specific part of the type in the error message.
|
// specific part of the type in the error message.
|
||||||
struct TyPathVisitor<'tcx> {
|
struct TyPathVisitor<'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
found_it: bool,
|
|
||||||
bound_region: ty::BoundRegionKind,
|
bound_region: ty::BoundRegionKind,
|
||||||
current_index: ty::DebruijnIndex,
|
current_index: ty::DebruijnIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
|
impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
|
||||||
|
type Result = ControlFlow<()>;
|
||||||
type NestedFilter = nested_filter::OnlyBodies;
|
type NestedFilter = nested_filter::OnlyBodies;
|
||||||
|
|
||||||
fn nested_visit_map(&mut self) -> Map<'tcx> {
|
fn nested_visit_map(&mut self) -> Map<'tcx> {
|
||||||
self.tcx.hir()
|
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) {
|
match (self.tcx.named_bound_var(lifetime.hir_id), self.bound_region) {
|
||||||
// the lifetime of the TyPath!
|
// the lifetime of the TyPath!
|
||||||
(Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
|
(Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
|
||||||
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
|
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
|
||||||
if id == def_id {
|
if id == def_id {
|
||||||
self.found_it = true;
|
return ControlFlow::Break(());
|
||||||
return; // we can stop visiting now
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,8 +199,7 @@ fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
|
|||||||
debug!("id={:?}", id);
|
debug!("id={:?}", id);
|
||||||
debug!("def_id={:?}", def_id);
|
debug!("def_id={:?}", def_id);
|
||||||
if debruijn_index == self.current_index && id == def_id {
|
if debruijn_index == self.current_index && id == def_id {
|
||||||
self.found_it = true;
|
return ControlFlow::Break(());
|
||||||
return; // we can stop visiting now
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,9 +217,10 @@ fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
|
|||||||
debug!("no arg found");
|
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
|
// ignore nested types
|
||||||
//
|
//
|
||||||
// If you have a type like `Foo<'a, &Ty>` we
|
// If you have a type like `Foo<'a, &Ty>` we
|
||||||
@ -231,5 +229,6 @@ fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
|
|||||||
// Making `visit_ty` empty will ignore the `&Ty` embedded
|
// Making `visit_ty` empty will ignore the `&Ty` embedded
|
||||||
// inside, it will get reached by the outer visitor.
|
// inside, it will get reached by the outer visitor.
|
||||||
debug!("`Ty` corresponding to a struct is {:?}", arg);
|
debug!("`Ty` corresponding to a struct is {:?}", arg);
|
||||||
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::infer::error_reporting::hir::Path;
|
use crate::infer::error_reporting::hir::Path;
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use hir::def::CtorKind;
|
use hir::def::CtorKind;
|
||||||
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
||||||
use hir::{Local, QPath};
|
use hir::{Local, QPath};
|
||||||
@ -563,62 +564,55 @@ pub(super) fn suggest_let_for_letchains(
|
|||||||
cause: &ObligationCause<'_>,
|
cause: &ObligationCause<'_>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Option<TypeErrorAdditionalDiags> {
|
) -> Option<TypeErrorAdditionalDiags> {
|
||||||
let hir = self.tcx.hir();
|
/// Find the if expression with given span
|
||||||
if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(cause.body_id) {
|
struct IfVisitor {
|
||||||
let body = hir.body(body_id);
|
pub found_if: bool,
|
||||||
|
pub err_span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
/// Find the if expression with given span
|
impl<'v> Visitor<'v> for IfVisitor {
|
||||||
struct IfVisitor {
|
type Result = ControlFlow<()>;
|
||||||
pub result: bool,
|
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
|
||||||
pub found_if: bool,
|
match ex.kind {
|
||||||
pub err_span: Span,
|
hir::ExprKind::If(cond, _, _) => {
|
||||||
}
|
self.found_if = true;
|
||||||
|
walk_expr(self, cond)?;
|
||||||
impl<'v> Visitor<'v> for IfVisitor {
|
self.found_if = false;
|
||||||
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
|
ControlFlow::Continue(())
|
||||||
if self.result {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
match ex.kind {
|
_ => walk_expr(self, ex),
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
|
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
|
||||||
visitor.visit_body(body);
|
if let hir::StmtKind::Local(hir::Local {
|
||||||
if visitor.result {
|
span,
|
||||||
return Some(TypeErrorAdditionalDiags::AddLetForLetChains {
|
pat: hir::Pat { .. },
|
||||||
span: span.shrink_to_lo(),
|
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
|
/// For "one type is more general than the other" errors on closures, suggest changing the lifetime
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#![allow(rustc::untranslatable_diagnostic)]
|
#![allow(rustc::untranslatable_diagnostic)]
|
||||||
#![feature(associated_type_bounds)]
|
#![feature(associated_type_bounds)]
|
||||||
#![feature(box_patterns)]
|
#![feature(box_patterns)]
|
||||||
|
#![feature(control_flow_enum)]
|
||||||
#![feature(extend_one)]
|
#![feature(extend_one)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![feature(if_let_guard)]
|
#![feature(if_let_guard)]
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
ObligationCause, ObligationCauseCode, ObligationCtxt, Overflow, PredicateObligation,
|
ObligationCause, ObligationCauseCode, ObligationCtxt, Overflow, PredicateObligation,
|
||||||
SelectionError, SignatureMismatch, TraitNotObjectSafe,
|
SelectionError, SignatureMismatch, TraitNotObjectSafe,
|
||||||
};
|
};
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||||
use rustc_errors::codes::*;
|
use rustc_errors::codes::*;
|
||||||
use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart};
|
use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart};
|
||||||
@ -1126,22 +1127,20 @@ fn try_conversion_context(
|
|||||||
err: &mut Diag<'_>,
|
err: &mut Diag<'_>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let span = obligation.cause.span;
|
let span = obligation.cause.span;
|
||||||
struct V<'v> {
|
struct V {
|
||||||
search_span: Span,
|
search_span: Span,
|
||||||
found: Option<&'v hir::Expr<'v>>,
|
|
||||||
}
|
}
|
||||||
impl<'v> Visitor<'v> for V<'v> {
|
impl<'v> Visitor<'v> for V {
|
||||||
fn visit_expr(&mut self, ex: &'v hir::Expr<'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
|
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) {
|
ControlFlow::Break(expr)
|
||||||
if let hir::ExprKind::Call(_, [expr, ..]) = expr.kind {
|
} else {
|
||||||
self.found = Some(expr);
|
hir::intravisit::walk_expr(self, ex)
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
hir::intravisit::walk_expr(self, ex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id);
|
let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id);
|
||||||
@ -1149,9 +1148,9 @@ fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
|
|||||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => body_id,
|
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => body_id,
|
||||||
_ => return false,
|
_ => return false,
|
||||||
};
|
};
|
||||||
let mut v = V { search_span: span, found: None };
|
let ControlFlow::Break(expr) =
|
||||||
v.visit_body(self.tcx.hir().body(*body_id));
|
(V { search_span: span }).visit_body(self.tcx.hir().body(*body_id))
|
||||||
let Some(expr) = v.found else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let Some(typeck) = &self.typeck_results else {
|
let Some(typeck) = &self.typeck_results else {
|
||||||
|
@ -4,11 +4,11 @@ error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` d
|
|||||||
LL | fn subscribe(&mut self, t : Box<dyn Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
|
LL | fn subscribe(&mut self, t : Box<dyn Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
note: first, the lifetime cannot outlive the anonymous lifetime defined here...
|
note: first, the lifetime cannot outlive the anonymous lifetime as defined here...
|
||||||
--> $DIR/issue-20831-debruijn.rs:28:58
|
--> $DIR/issue-20831-debruijn.rs:28:18
|
||||||
|
|
|
|
||||||
LL | fn subscribe(&mut self, t : Box<dyn Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
|
LL | fn subscribe(&mut self, t : Box<dyn Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^
|
||||||
note: ...but the lifetime must also be valid for the lifetime `'a` as defined here...
|
note: ...but the lifetime must also be valid for the lifetime `'a` as defined here...
|
||||||
--> $DIR/issue-20831-debruijn.rs:26:6
|
--> $DIR/issue-20831-debruijn.rs:26:6
|
||||||
|
|
|
|
||||||
|
Loading…
Reference in New Issue
Block a user