Auto merge of #122511 - matthiaskrgr:rollup-swzilin, r=matthiaskrgr
Rollup of 10 pull requests Successful merges: - #117118 ([AIX] Remove AixLinker's debuginfo() implementation) - #121650 (change std::process to drop supplementary groups based on CAP_SETGID) - #121764 (Make incremental sessions identity no longer depend on the crate names provided by source code) - #122212 (Copy byval argument to alloca if alignment is insufficient) - #122322 (coverage: Initial support for branch coverage instrumentation) - #122373 (Fix the conflict problem between the diagnostics fixes of lint `unnecessary_qualification` and `unused_imports`) - #122479 (Implement `Duration::as_millis_{f64,f32}`) - #122487 (Rename `StmtKind::Local` variant into `StmtKind::Let`) - #122498 (Update version of cc crate) - #122503 (Make `SubdiagMessageOp` well-formed) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
c2901f5435
143 changed files with 2023 additions and 393 deletions
|
@ -1021,7 +1021,7 @@ impl Stmt {
|
|||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub enum StmtKind {
|
||||
/// A local (let) binding.
|
||||
Local(P<Local>),
|
||||
Let(P<Local>),
|
||||
/// An item definition.
|
||||
Item(P<Item>),
|
||||
/// Expr without trailing semi-colon.
|
||||
|
|
|
@ -182,7 +182,7 @@ impl<T: HasTokens> HasTokens for Option<T> {
|
|||
impl HasTokens for StmtKind {
|
||||
fn tokens(&self) -> Option<&LazyAttrTokenStream> {
|
||||
match self {
|
||||
StmtKind::Local(local) => local.tokens.as_ref(),
|
||||
StmtKind::Let(local) => local.tokens.as_ref(),
|
||||
StmtKind::Item(item) => item.tokens(),
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.tokens(),
|
||||
StmtKind::Empty => return None,
|
||||
|
@ -191,7 +191,7 @@ impl HasTokens for StmtKind {
|
|||
}
|
||||
fn tokens_mut(&mut self) -> Option<&mut Option<LazyAttrTokenStream>> {
|
||||
match self {
|
||||
StmtKind::Local(local) => Some(&mut local.tokens),
|
||||
StmtKind::Let(local) => Some(&mut local.tokens),
|
||||
StmtKind::Item(item) => item.tokens_mut(),
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.tokens_mut(),
|
||||
StmtKind::Empty => return None,
|
||||
|
@ -355,7 +355,7 @@ impl HasAttrs for StmtKind {
|
|||
|
||||
fn attrs(&self) -> &[Attribute] {
|
||||
match self {
|
||||
StmtKind::Local(local) => &local.attrs,
|
||||
StmtKind::Let(local) => &local.attrs,
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(),
|
||||
StmtKind::Item(item) => item.attrs(),
|
||||
StmtKind::Empty => &[],
|
||||
|
@ -365,7 +365,7 @@ impl HasAttrs for StmtKind {
|
|||
|
||||
fn visit_attrs(&mut self, f: impl FnOnce(&mut AttrVec)) {
|
||||
match self {
|
||||
StmtKind::Local(local) => f(&mut local.attrs),
|
||||
StmtKind::Let(local) => f(&mut local.attrs),
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f),
|
||||
StmtKind::Item(item) => item.visit_attrs(f),
|
||||
StmtKind::Empty => {}
|
||||
|
|
|
@ -1567,7 +1567,7 @@ pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
|
|||
vis: &mut T,
|
||||
) -> SmallVec<[StmtKind; 1]> {
|
||||
match kind {
|
||||
StmtKind::Local(mut local) => smallvec![StmtKind::Local({
|
||||
StmtKind::Let(mut local) => smallvec![StmtKind::Let({
|
||||
vis.visit_local(&mut local);
|
||||
local
|
||||
})],
|
||||
|
|
|
@ -787,7 +787,7 @@ pub fn walk_block<'a, V: Visitor<'a>>(visitor: &mut V, block: &'a Block) -> V::R
|
|||
|
||||
pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) -> V::Result {
|
||||
match &statement.kind {
|
||||
StmtKind::Local(local) => try_visit!(visitor.visit_local(local)),
|
||||
StmtKind::Let(local) => try_visit!(visitor.visit_local(local)),
|
||||
StmtKind::Item(item) => try_visit!(visitor.visit_item(item)),
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => try_visit!(visitor.visit_expr(expr)),
|
||||
StmtKind::Empty => {}
|
||||
|
|
|
@ -32,11 +32,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let mut expr = None;
|
||||
while let [s, tail @ ..] = ast_stmts {
|
||||
match &s.kind {
|
||||
StmtKind::Local(local) => {
|
||||
StmtKind::Let(local) => {
|
||||
let hir_id = self.lower_node_id(s.id);
|
||||
let local = self.lower_local(local);
|
||||
self.alias_attrs(hir_id, local.hir_id);
|
||||
let kind = hir::StmtKind::Local(local);
|
||||
let kind = hir::StmtKind::Let(local);
|
||||
let span = self.lower_span(s.span);
|
||||
stmts.push(hir::Stmt { hir_id, kind, span });
|
||||
}
|
||||
|
|
|
@ -2356,7 +2356,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
span: self.lower_span(span),
|
||||
ty: None,
|
||||
};
|
||||
self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local)))
|
||||
self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
|
||||
}
|
||||
|
||||
fn block_expr(&mut self, expr: &'hir hir::Expr<'hir>) -> &'hir hir::Block<'hir> {
|
||||
|
|
|
@ -1212,7 +1212,7 @@ impl<'a> State<'a> {
|
|||
fn print_stmt(&mut self, st: &ast::Stmt) {
|
||||
self.maybe_print_comment(st.span.lo());
|
||||
match &st.kind {
|
||||
ast::StmtKind::Local(loc) => {
|
||||
ast::StmtKind::Let(loc) => {
|
||||
self.print_outer_attributes(&loc.attrs);
|
||||
self.space_if_not_bol();
|
||||
self.ibox(INDENT_UNIT);
|
||||
|
|
|
@ -616,7 +616,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
|
||||
// FIXME: We make sure that this is a normal top-level binding,
|
||||
// but we could suggest `todo!()` for all uninitalized bindings in the pattern pattern
|
||||
if let hir::StmtKind::Local(hir::Local { span, ty, init: None, pat, .. }) =
|
||||
if let hir::StmtKind::Let(hir::Local { span, ty, init: None, pat, .. }) =
|
||||
&ex.kind
|
||||
&& let hir::PatKind::Binding(..) = pat.kind
|
||||
&& span.contains(self.decl_span)
|
||||
|
|
|
@ -558,7 +558,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
hir::intravisit::walk_stmt(self, stmt);
|
||||
let expr = match stmt.kind {
|
||||
hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr,
|
||||
hir::StmtKind::Local(hir::Local { init: Some(expr), .. }) => expr,
|
||||
hir::StmtKind::Let(hir::Local { init: Some(expr), .. }) => expr,
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
|
@ -1305,7 +1305,7 @@ struct BindingFinder {
|
|||
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
|
||||
if let hir::StmtKind::Let(local) = s.kind
|
||||
&& local.pat.span == self.span
|
||||
{
|
||||
ControlFlow::Break(local.hir_id)
|
||||
|
|
|
@ -203,57 +203,63 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
|
|||
val: &'ll Value,
|
||||
dst: PlaceRef<'tcx, &'ll Value>,
|
||||
) {
|
||||
if self.is_ignore() {
|
||||
return;
|
||||
}
|
||||
if self.is_sized_indirect() {
|
||||
OperandValue::Ref(val, None, self.layout.align.abi).store(bx, dst)
|
||||
} else if self.is_unsized_indirect() {
|
||||
bug!("unsized `ArgAbi` must be handled through `store_fn_arg`");
|
||||
} else if let PassMode::Cast { cast, pad_i32: _ } = &self.mode {
|
||||
// FIXME(eddyb): Figure out when the simpler Store is safe, clang
|
||||
// uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
|
||||
let can_store_through_cast_ptr = false;
|
||||
if can_store_through_cast_ptr {
|
||||
bx.store(val, dst.llval, self.layout.align.abi);
|
||||
} else {
|
||||
// The actual return type is a struct, but the ABI
|
||||
// adaptation code has cast it into some scalar type. The
|
||||
// code that follows is the only reliable way I have
|
||||
// found to do a transform like i64 -> {i32,i32}.
|
||||
// Basically we dump the data onto the stack then memcpy it.
|
||||
//
|
||||
// Other approaches I tried:
|
||||
// - Casting rust ret pointer to the foreign type and using Store
|
||||
// is (a) unsafe if size of foreign type > size of rust type and
|
||||
// (b) runs afoul of strict aliasing rules, yielding invalid
|
||||
// assembly under -O (specifically, the store gets removed).
|
||||
// - Truncating foreign type to correct integral type and then
|
||||
// bitcasting to the struct type yields invalid cast errors.
|
||||
|
||||
// We instead thus allocate some scratch space...
|
||||
let scratch_size = cast.size(bx);
|
||||
let scratch_align = cast.align(bx);
|
||||
let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align);
|
||||
bx.lifetime_start(llscratch, scratch_size);
|
||||
|
||||
// ... where we first store the value...
|
||||
bx.store(val, llscratch, scratch_align);
|
||||
|
||||
// ... and then memcpy it to the intended destination.
|
||||
bx.memcpy(
|
||||
dst.llval,
|
||||
self.layout.align.abi,
|
||||
llscratch,
|
||||
scratch_align,
|
||||
bx.const_usize(self.layout.size.bytes()),
|
||||
MemFlags::empty(),
|
||||
);
|
||||
|
||||
bx.lifetime_end(llscratch, scratch_size);
|
||||
match &self.mode {
|
||||
PassMode::Ignore => {}
|
||||
// Sized indirect arguments
|
||||
PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => {
|
||||
let align = attrs.pointee_align.unwrap_or(self.layout.align.abi);
|
||||
OperandValue::Ref(val, None, align).store(bx, dst);
|
||||
}
|
||||
// Unsized indirect qrguments
|
||||
PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
|
||||
bug!("unsized `ArgAbi` must be handled through `store_fn_arg`");
|
||||
}
|
||||
PassMode::Cast { cast, pad_i32: _ } => {
|
||||
// FIXME(eddyb): Figure out when the simpler Store is safe, clang
|
||||
// uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
|
||||
let can_store_through_cast_ptr = false;
|
||||
if can_store_through_cast_ptr {
|
||||
bx.store(val, dst.llval, self.layout.align.abi);
|
||||
} else {
|
||||
// The actual return type is a struct, but the ABI
|
||||
// adaptation code has cast it into some scalar type. The
|
||||
// code that follows is the only reliable way I have
|
||||
// found to do a transform like i64 -> {i32,i32}.
|
||||
// Basically we dump the data onto the stack then memcpy it.
|
||||
//
|
||||
// Other approaches I tried:
|
||||
// - Casting rust ret pointer to the foreign type and using Store
|
||||
// is (a) unsafe if size of foreign type > size of rust type and
|
||||
// (b) runs afoul of strict aliasing rules, yielding invalid
|
||||
// assembly under -O (specifically, the store gets removed).
|
||||
// - Truncating foreign type to correct integral type and then
|
||||
// bitcasting to the struct type yields invalid cast errors.
|
||||
|
||||
// We instead thus allocate some scratch space...
|
||||
let scratch_size = cast.size(bx);
|
||||
let scratch_align = cast.align(bx);
|
||||
let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align);
|
||||
bx.lifetime_start(llscratch, scratch_size);
|
||||
|
||||
// ... where we first store the value...
|
||||
bx.store(val, llscratch, scratch_align);
|
||||
|
||||
// ... and then memcpy it to the intended destination.
|
||||
bx.memcpy(
|
||||
dst.llval,
|
||||
self.layout.align.abi,
|
||||
llscratch,
|
||||
scratch_align,
|
||||
bx.const_usize(self.layout.size.bytes()),
|
||||
MemFlags::empty(),
|
||||
);
|
||||
|
||||
bx.lifetime_end(llscratch, scratch_size);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
OperandRef::from_immediate_or_packed_pair(bx, val, self.layout).val.store(bx, dst);
|
||||
}
|
||||
} else {
|
||||
OperandRef::from_immediate_or_packed_pair(bx, val, self.layout).val.store(bx, dst);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -164,6 +164,15 @@ impl CounterMappingRegion {
|
|||
end_line,
|
||||
end_col,
|
||||
),
|
||||
MappingKind::Branch { true_term, false_term } => Self::branch_region(
|
||||
Counter::from_term(true_term),
|
||||
Counter::from_term(false_term),
|
||||
local_file_id,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,9 +197,6 @@ impl CounterMappingRegion {
|
|||
}
|
||||
}
|
||||
|
||||
// This function might be used in the future; the LLVM API is still evolving, as is coverage
|
||||
// support.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn branch_region(
|
||||
counter: Counter,
|
||||
false_counter: Counter,
|
||||
|
|
|
@ -88,7 +88,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
|||
match coverage.kind {
|
||||
// Marker statements have no effect during codegen,
|
||||
// so return early and don't create `func_coverage`.
|
||||
CoverageKind::SpanMarker => return,
|
||||
CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => return,
|
||||
// Match exhaustively to ensure that newly-added kinds are classified correctly.
|
||||
CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } => {}
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
|||
|
||||
let Coverage { kind } = coverage;
|
||||
match *kind {
|
||||
CoverageKind::SpanMarker => unreachable!(
|
||||
CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!(
|
||||
"unexpected marker statement {kind:?} should have caused an early return"
|
||||
),
|
||||
CoverageKind::CounterIncrement { id } => {
|
||||
|
|
|
@ -1081,6 +1081,21 @@ fn link_natively<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
if sess.target.is_like_aix {
|
||||
let stripcmd = "/usr/bin/strip";
|
||||
match strip {
|
||||
Strip::Debuginfo => {
|
||||
// FIXME: AIX's strip utility only offers option to strip line number information.
|
||||
strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-l"))
|
||||
}
|
||||
Strip::Symbols => {
|
||||
// Must be noted this option might remove symbol __aix_rust_metadata and thus removes .info section which contains metadata.
|
||||
strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-r"))
|
||||
}
|
||||
Strip::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1640,16 +1640,7 @@ impl<'a> Linker for AixLinker<'a> {
|
|||
|
||||
fn ehcont_guard(&mut self) {}
|
||||
|
||||
fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
|
||||
match strip {
|
||||
Strip::None => {}
|
||||
// FIXME: -s strips the symbol table, line number information
|
||||
// and relocation information.
|
||||
Strip::Debuginfo | Strip::Symbols => {
|
||||
self.cmd.arg("-s");
|
||||
}
|
||||
}
|
||||
}
|
||||
fn debuginfo(&mut self, _: Strip, _: &[PathBuf]) {}
|
||||
|
||||
fn no_crt_objects(&mut self) {}
|
||||
|
||||
|
|
|
@ -377,29 +377,45 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
}
|
||||
}
|
||||
|
||||
if arg.is_sized_indirect() {
|
||||
// Don't copy an indirect argument to an alloca, the caller
|
||||
// already put it in a temporary alloca and gave it up.
|
||||
// FIXME: lifetimes
|
||||
let llarg = bx.get_param(llarg_idx);
|
||||
llarg_idx += 1;
|
||||
LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout))
|
||||
} else if arg.is_unsized_indirect() {
|
||||
// As the storage for the indirect argument lives during
|
||||
// the whole function call, we just copy the fat pointer.
|
||||
let llarg = bx.get_param(llarg_idx);
|
||||
llarg_idx += 1;
|
||||
let llextra = bx.get_param(llarg_idx);
|
||||
llarg_idx += 1;
|
||||
let indirect_operand = OperandValue::Pair(llarg, llextra);
|
||||
match arg.mode {
|
||||
// Sized indirect arguments
|
||||
PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => {
|
||||
// Don't copy an indirect argument to an alloca, the caller already put it
|
||||
// in a temporary alloca and gave it up.
|
||||
// FIXME: lifetimes
|
||||
if let Some(pointee_align) = attrs.pointee_align
|
||||
&& pointee_align < arg.layout.align.abi
|
||||
{
|
||||
// ...unless the argument is underaligned, then we need to copy it to
|
||||
// a higher-aligned alloca.
|
||||
let tmp = PlaceRef::alloca(bx, arg.layout);
|
||||
bx.store_fn_arg(arg, &mut llarg_idx, tmp);
|
||||
LocalRef::Place(tmp)
|
||||
} else {
|
||||
let llarg = bx.get_param(llarg_idx);
|
||||
llarg_idx += 1;
|
||||
LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout))
|
||||
}
|
||||
}
|
||||
// Unsized indirect qrguments
|
||||
PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
|
||||
// As the storage for the indirect argument lives during
|
||||
// the whole function call, we just copy the fat pointer.
|
||||
let llarg = bx.get_param(llarg_idx);
|
||||
llarg_idx += 1;
|
||||
let llextra = bx.get_param(llarg_idx);
|
||||
llarg_idx += 1;
|
||||
let indirect_operand = OperandValue::Pair(llarg, llextra);
|
||||
|
||||
let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout);
|
||||
indirect_operand.store(bx, tmp);
|
||||
LocalRef::UnsizedPlace(tmp)
|
||||
} else {
|
||||
let tmp = PlaceRef::alloca(bx, arg.layout);
|
||||
bx.store_fn_arg(arg, &mut llarg_idx, tmp);
|
||||
LocalRef::Place(tmp)
|
||||
let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout);
|
||||
indirect_operand.store(bx, tmp);
|
||||
LocalRef::UnsizedPlace(tmp)
|
||||
}
|
||||
_ => {
|
||||
let tmp = PlaceRef::alloca(bx, arg.layout);
|
||||
bx.store_fn_arg(arg, &mut llarg_idx, tmp);
|
||||
LocalRef::Place(tmp)
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
|
|
@ -28,7 +28,7 @@ use rustc_errors::{
|
|||
markdown, ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult,
|
||||
};
|
||||
use rustc_feature::find_gated_cfg;
|
||||
use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
|
||||
use rustc_interface::util::{self, get_codegen_backend};
|
||||
use rustc_interface::{interface, Queries};
|
||||
use rustc_lint::unerased_lint_store;
|
||||
use rustc_metadata::creader::MetadataLoader;
|
||||
|
@ -37,6 +37,7 @@ use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS};
|
|||
use rustc_session::config::{ErrorOutputType, Input, OutFileName, OutputType};
|
||||
use rustc_session::getopts::{self, Matches};
|
||||
use rustc_session::lint::{Lint, LintId};
|
||||
use rustc_session::output::collect_crate_types;
|
||||
use rustc_session::{config, filesearch, EarlyDiagCtxt, Session};
|
||||
use rustc_span::def_id::LOCAL_CRATE;
|
||||
use rustc_span::source_map::FileLoader;
|
||||
|
|
|
@ -189,7 +189,8 @@ where
|
|||
);
|
||||
}
|
||||
|
||||
pub trait SubdiagMessageOp<G> = Fn(&mut Diag<'_, G>, SubdiagMessage) -> SubdiagMessage;
|
||||
pub trait SubdiagMessageOp<G: EmissionGuarantee> =
|
||||
Fn(&mut Diag<'_, G>, SubdiagMessage) -> SubdiagMessage;
|
||||
|
||||
/// Trait implemented by lint types. This should not be implemented manually. Instead, use
|
||||
/// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic].
|
||||
|
|
|
@ -218,7 +218,7 @@ impl<'a> ExtCtxt<'a> {
|
|||
}
|
||||
|
||||
pub fn stmt_local(&self, local: P<ast::Local>, span: Span) -> ast::Stmt {
|
||||
ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span }
|
||||
ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Let(local), span }
|
||||
}
|
||||
|
||||
pub fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt {
|
||||
|
|
|
@ -1389,7 +1389,7 @@ impl InvocationCollectorNode for ast::Stmt {
|
|||
StmtKind::Item(item) => matches!(item.kind, ItemKind::MacCall(..)),
|
||||
StmtKind::Semi(expr) => matches!(expr.kind, ExprKind::MacCall(..)),
|
||||
StmtKind::Expr(..) => unreachable!(),
|
||||
StmtKind::Local(..) | StmtKind::Empty => false,
|
||||
StmtKind::Let(..) | StmtKind::Empty => false,
|
||||
}
|
||||
}
|
||||
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
|
||||
|
|
|
@ -1209,7 +1209,7 @@ pub struct Stmt<'hir> {
|
|||
#[derive(Debug, Clone, Copy, HashStable_Generic)]
|
||||
pub enum StmtKind<'hir> {
|
||||
/// A local (`let`) binding.
|
||||
Local(&'hir Local<'hir>),
|
||||
Let(&'hir Local<'hir>),
|
||||
|
||||
/// An item binding.
|
||||
Item(ItemId),
|
||||
|
|
|
@ -627,7 +627,7 @@ pub fn walk_block<'v, V: Visitor<'v>>(visitor: &mut V, block: &'v Block<'v>) ->
|
|||
pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt<'v>) -> V::Result {
|
||||
try_visit!(visitor.visit_id(statement.hir_id));
|
||||
match statement.kind {
|
||||
StmtKind::Local(ref local) => visitor.visit_local(local),
|
||||
StmtKind::Let(ref local) => visitor.visit_local(local),
|
||||
StmtKind::Item(item) => visitor.visit_nested_item(item),
|
||||
StmtKind::Expr(ref expression) | StmtKind::Semi(ref expression) => {
|
||||
visitor.visit_expr(expression)
|
||||
|
|
|
@ -27,7 +27,7 @@ pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) {
|
|||
|
||||
/// Check for shared or mutable references of `static mut` inside statement
|
||||
pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) {
|
||||
if let hir::StmtKind::Local(loc) = stmt.kind
|
||||
if let hir::StmtKind::Let(loc) = stmt.kind
|
||||
&& let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind
|
||||
&& matches!(ba.0, rustc_ast::ByRef::Yes)
|
||||
&& let Some(init) = loc.init
|
||||
|
|
|
@ -123,7 +123,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
|
|||
|
||||
for (i, statement) in blk.stmts.iter().enumerate() {
|
||||
match statement.kind {
|
||||
hir::StmtKind::Local(hir::Local { els: Some(els), .. }) => {
|
||||
hir::StmtKind::Let(hir::Local { els: Some(els), .. }) => {
|
||||
// Let-else has a special lexical structure for variables.
|
||||
// First we take a checkpoint of the current scope context here.
|
||||
let mut prev_cx = visitor.cx;
|
||||
|
@ -146,7 +146,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
|
|||
// From now on, we continue normally.
|
||||
visitor.cx = prev_cx;
|
||||
}
|
||||
hir::StmtKind::Local(..) => {
|
||||
hir::StmtKind::Let(..) => {
|
||||
// Each declaration introduces a subscope for bindings
|
||||
// introduced by the declaration; this subscope covers a
|
||||
// suffix of the block. Each subscope in a block has the
|
||||
|
|
|
@ -864,7 +864,7 @@ impl<'a> State<'a> {
|
|||
fn print_stmt(&mut self, st: &hir::Stmt<'_>) {
|
||||
self.maybe_print_comment(st.span.lo());
|
||||
match st.kind {
|
||||
hir::StmtKind::Local(loc) => {
|
||||
hir::StmtKind::Let(loc) => {
|
||||
self.print_local(loc.init, loc.els, |this| this.print_local_decl(loc));
|
||||
}
|
||||
hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)),
|
||||
|
@ -2307,7 +2307,7 @@ fn expr_requires_semi_to_be_stmt(e: &hir::Expr<'_>) -> bool {
|
|||
/// seen the semicolon, and thus don't need another.
|
||||
fn stmt_ends_with_semi(stmt: &hir::StmtKind<'_>) -> bool {
|
||||
match *stmt {
|
||||
hir::StmtKind::Local(_) => true,
|
||||
hir::StmtKind::Let(_) => true,
|
||||
hir::StmtKind::Item(_) => false,
|
||||
hir::StmtKind::Expr(e) => expr_requires_semi_to_be_stmt(e),
|
||||
hir::StmtKind::Semi(..) => false,
|
||||
|
|
|
@ -371,11 +371,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
|
||||
fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) {
|
||||
match stmt.kind {
|
||||
hir::StmtKind::Local(hir::Local { pat, init: Some(expr), els, .. }) => {
|
||||
hir::StmtKind::Let(hir::Local { pat, init: Some(expr), els, .. }) => {
|
||||
self.walk_local(expr, pat, *els, |_| {})
|
||||
}
|
||||
|
||||
hir::StmtKind::Local(_) => {}
|
||||
hir::StmtKind::Let(_) => {}
|
||||
|
||||
hir::StmtKind::Item(_) => {
|
||||
// We don't visit nested items in this visitor,
|
||||
|
|
|
@ -1593,7 +1593,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// Don't do all the complex logic below for `DeclItem`.
|
||||
match stmt.kind {
|
||||
hir::StmtKind::Item(..) => return,
|
||||
hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {}
|
||||
hir::StmtKind::Let(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {}
|
||||
}
|
||||
|
||||
self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement");
|
||||
|
@ -1602,7 +1602,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let old_diverges = self.diverges.replace(Diverges::Maybe);
|
||||
|
||||
match stmt.kind {
|
||||
hir::StmtKind::Local(l) => {
|
||||
hir::StmtKind::Let(l) => {
|
||||
self.check_decl_local(l);
|
||||
}
|
||||
// Ignore for now.
|
||||
|
@ -1765,7 +1765,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
[
|
||||
hir::Stmt {
|
||||
kind:
|
||||
hir::StmtKind::Local(hir::Local {
|
||||
hir::StmtKind::Let(hir::Local {
|
||||
source:
|
||||
hir::LocalSource::AssignDesugar(_),
|
||||
..
|
||||
|
|
|
@ -1599,7 +1599,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
fn is_local_statement(&self, id: hir::HirId) -> bool {
|
||||
let node = self.tcx.hir_node(id);
|
||||
matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
|
||||
matches!(node, Node::Stmt(Stmt { kind: StmtKind::Let(..), .. }))
|
||||
}
|
||||
|
||||
/// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`,
|
||||
|
|
|
@ -2221,7 +2221,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
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
|
||||
if let hir::StmtKind::Let(&hir::Local { pat, init, .. }) = ex.kind
|
||||
&& let Binding(_, _, ident, ..) = pat.kind
|
||||
&& ident.name == self.ident_name
|
||||
{
|
||||
|
|
|
@ -217,7 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
bug!();
|
||||
};
|
||||
for stmt in block.stmts {
|
||||
let hir::StmtKind::Local(hir::Local {
|
||||
let hir::StmtKind::Let(hir::Local {
|
||||
init: Some(init),
|
||||
source: hir::LocalSource::AsyncFn,
|
||||
pat,
|
||||
|
|
|
@ -110,8 +110,9 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
|
|||
use rustc_data_structures::{base_n, flock};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_fs_util::{link_or_copy, try_canonicalize, LinkOrCopy};
|
||||
use rustc_session::config::CrateType;
|
||||
use rustc_session::output::{collect_crate_types, find_crate_name};
|
||||
use rustc_session::{Session, StableCrateId};
|
||||
use rustc_span::Symbol;
|
||||
|
||||
use std::fs as std_fs;
|
||||
use std::io::{self, ErrorKind};
|
||||
|
@ -205,11 +206,7 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu
|
|||
/// The garbage collection will take care of it.
|
||||
///
|
||||
/// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph
|
||||
pub(crate) fn prepare_session_directory(
|
||||
sess: &Session,
|
||||
crate_name: Symbol,
|
||||
stable_crate_id: StableCrateId,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuaranteed> {
|
||||
if sess.opts.incremental.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -219,7 +216,7 @@ pub(crate) fn prepare_session_directory(
|
|||
debug!("prepare_session_directory");
|
||||
|
||||
// {incr-comp-dir}/{crate-name-and-disambiguator}
|
||||
let crate_dir = crate_path(sess, crate_name, stable_crate_id);
|
||||
let crate_dir = crate_path(sess);
|
||||
debug!("crate-dir: {}", crate_dir.display());
|
||||
create_dir(sess, &crate_dir, "crate")?;
|
||||
|
||||
|
@ -604,9 +601,18 @@ fn string_to_timestamp(s: &str) -> Result<SystemTime, &'static str> {
|
|||
Ok(UNIX_EPOCH + duration)
|
||||
}
|
||||
|
||||
fn crate_path(sess: &Session, crate_name: Symbol, stable_crate_id: StableCrateId) -> PathBuf {
|
||||
fn crate_path(sess: &Session) -> PathBuf {
|
||||
let incr_dir = sess.opts.incremental.as_ref().unwrap().clone();
|
||||
|
||||
let crate_name = find_crate_name(sess, &[]);
|
||||
let crate_types = collect_crate_types(sess, &[]);
|
||||
let stable_crate_id = StableCrateId::new(
|
||||
crate_name,
|
||||
crate_types.contains(&CrateType::Executable),
|
||||
sess.opts.cg.metadata.clone(),
|
||||
sess.cfg_version,
|
||||
);
|
||||
|
||||
let stable_crate_id = base_n::encode(stable_crate_id.as_u64() as u128, INT_ENCODE_BASE);
|
||||
|
||||
let crate_name = format!("{crate_name}-{stable_crate_id}");
|
||||
|
|
|
@ -8,8 +8,8 @@ use rustc_middle::query::on_disk_cache::OnDiskCache;
|
|||
use rustc_serialize::opaque::MemDecoder;
|
||||
use rustc_serialize::Decodable;
|
||||
use rustc_session::config::IncrementalStateAssertion;
|
||||
use rustc_session::{Session, StableCrateId};
|
||||
use rustc_span::{ErrorGuaranteed, Symbol};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::ErrorGuaranteed;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use super::data::*;
|
||||
|
@ -190,13 +190,9 @@ pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache<'_>> {
|
|||
|
||||
/// Setups the dependency graph by loading an existing graph from disk and set up streaming of a
|
||||
/// new graph to an incremental session directory.
|
||||
pub fn setup_dep_graph(
|
||||
sess: &Session,
|
||||
crate_name: Symbol,
|
||||
stable_crate_id: StableCrateId,
|
||||
) -> Result<DepGraph, ErrorGuaranteed> {
|
||||
pub fn setup_dep_graph(sess: &Session) -> Result<DepGraph, ErrorGuaranteed> {
|
||||
// `load_dep_graph` can only be called after `prepare_session_directory`.
|
||||
prepare_session_directory(sess, crate_name, stable_crate_id)?;
|
||||
prepare_session_directory(sess)?;
|
||||
|
||||
let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess));
|
||||
|
||||
|
|
|
@ -2139,7 +2139,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
// the same span as the error and the type is specified.
|
||||
if let hir::Stmt {
|
||||
kind:
|
||||
hir::StmtKind::Local(hir::Local {
|
||||
hir::StmtKind::Let(hir::Local {
|
||||
init: Some(hir::Expr { span: init_span, .. }),
|
||||
ty: Some(array_ty),
|
||||
..
|
||||
|
|
|
@ -585,7 +585,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
|
||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
|
||||
if let hir::StmtKind::Local(hir::Local {
|
||||
if let hir::StmtKind::Let(hir::Local {
|
||||
span,
|
||||
pat: hir::Pat { .. },
|
||||
ty: None,
|
||||
|
@ -824,7 +824,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
|
||||
let hir = self.tcx.hir();
|
||||
for stmt in blk.stmts.iter().rev() {
|
||||
let hir::StmtKind::Local(local) = &stmt.kind else {
|
||||
let hir::StmtKind::Let(local) = &stmt.kind else {
|
||||
continue;
|
||||
};
|
||||
local.pat.walk(&mut find_compatible_candidates);
|
||||
|
|
|
@ -48,6 +48,3 @@ interface_rustc_error_unexpected_annotation =
|
|||
|
||||
interface_temps_dir_error =
|
||||
failed to find or create the directory specified by `--temps-dir`
|
||||
|
||||
interface_unsupported_crate_type_for_target =
|
||||
dropping unsupported crate type `{$crate_type}` for target `{$target_triple}`
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use rustc_macros::Diagnostic;
|
||||
use rustc_session::config::CrateType;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_target::spec::TargetTriple;
|
||||
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
@ -90,13 +88,6 @@ pub struct FailedWritingFile<'a> {
|
|||
#[diag(interface_proc_macro_crate_panic_abort)]
|
||||
pub struct ProcMacroCratePanicAbort;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(interface_unsupported_crate_type_for_target)]
|
||||
pub struct UnsupportedCrateTypeForTarget<'a> {
|
||||
pub crate_type: CrateType,
|
||||
pub target_triple: &'a TargetTriple,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(interface_multiple_output_types_adaption)]
|
||||
pub struct MultipleOutputTypesAdaption;
|
||||
|
|
|
@ -18,7 +18,7 @@ use rustc_middle::ty::{GlobalCtxt, TyCtxt};
|
|||
use rustc_serialize::opaque::FileEncodeResult;
|
||||
use rustc_session::config::{self, CrateType, OutputFilenames, OutputType};
|
||||
use rustc_session::cstore::Untracked;
|
||||
use rustc_session::output::find_crate_name;
|
||||
use rustc_session::output::{collect_crate_types, find_crate_name};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::sym;
|
||||
use std::any::Any;
|
||||
|
@ -128,7 +128,7 @@ impl<'tcx> Queries<'tcx> {
|
|||
|
||||
// parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches.
|
||||
let crate_name = find_crate_name(sess, &pre_configured_attrs);
|
||||
let crate_types = util::collect_crate_types(sess, &pre_configured_attrs);
|
||||
let crate_types = collect_crate_types(sess, &pre_configured_attrs);
|
||||
let stable_crate_id = StableCrateId::new(
|
||||
crate_name,
|
||||
crate_types.contains(&CrateType::Executable),
|
||||
|
@ -136,7 +136,7 @@ impl<'tcx> Queries<'tcx> {
|
|||
sess.cfg_version,
|
||||
);
|
||||
let outputs = util::build_output_filenames(&pre_configured_attrs, sess);
|
||||
let dep_graph = setup_dep_graph(sess, crate_name, stable_crate_id)?;
|
||||
let dep_graph = setup_dep_graph(sess)?;
|
||||
|
||||
let cstore = FreezeLock::new(Box::new(CStore::new(
|
||||
self.compiler.codegen_backend.metadata_loader(),
|
||||
|
|
|
@ -7,14 +7,15 @@ use rustc_data_structures::sync;
|
|||
use rustc_metadata::{load_symbol_from_dylib, DylibError};
|
||||
use rustc_parse::validate_attr;
|
||||
use rustc_session as session;
|
||||
use rustc_session::config::{self, Cfg, CrateType, OutFileName, OutputFilenames, OutputTypes};
|
||||
use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes};
|
||||
use rustc_session::filesearch::sysroot_candidates;
|
||||
use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer};
|
||||
use rustc_session::{filesearch, output, Session};
|
||||
use rustc_session::{filesearch, Session};
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_target::spec::Target;
|
||||
use session::output::{categorize_crate_type, CRATE_TYPES};
|
||||
use session::EarlyDiagCtxt;
|
||||
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -399,67 +400,6 @@ pub(crate) fn check_attr_crate_type(
|
|||
}
|
||||
}
|
||||
|
||||
const CRATE_TYPES: &[(Symbol, CrateType)] = &[
|
||||
(sym::rlib, CrateType::Rlib),
|
||||
(sym::dylib, CrateType::Dylib),
|
||||
(sym::cdylib, CrateType::Cdylib),
|
||||
(sym::lib, config::default_lib_output()),
|
||||
(sym::staticlib, CrateType::Staticlib),
|
||||
(sym::proc_dash_macro, CrateType::ProcMacro),
|
||||
(sym::bin, CrateType::Executable),
|
||||
];
|
||||
|
||||
fn categorize_crate_type(s: Symbol) -> Option<CrateType> {
|
||||
Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1)
|
||||
}
|
||||
|
||||
pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<CrateType> {
|
||||
// If we're generating a test executable, then ignore all other output
|
||||
// styles at all other locations
|
||||
if session.opts.test {
|
||||
return vec![CrateType::Executable];
|
||||
}
|
||||
|
||||
// Only check command line flags if present. If no types are specified by
|
||||
// command line, then reuse the empty `base` Vec to hold the types that
|
||||
// will be found in crate attributes.
|
||||
// JUSTIFICATION: before wrapper fn is available
|
||||
#[allow(rustc::bad_opt_access)]
|
||||
let mut base = session.opts.crate_types.clone();
|
||||
if base.is_empty() {
|
||||
let attr_types = attrs.iter().filter_map(|a| {
|
||||
if a.has_name(sym::crate_type)
|
||||
&& let Some(s) = a.value_str()
|
||||
{
|
||||
categorize_crate_type(s)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
base.extend(attr_types);
|
||||
if base.is_empty() {
|
||||
base.push(output::default_output_for_target(session));
|
||||
} else {
|
||||
base.sort();
|
||||
base.dedup();
|
||||
}
|
||||
}
|
||||
|
||||
base.retain(|crate_type| {
|
||||
if output::invalid_output_for_target(session, *crate_type) {
|
||||
session.dcx().emit_warn(errors::UnsupportedCrateTypeForTarget {
|
||||
crate_type: *crate_type,
|
||||
target_triple: &session.opts.target_triple,
|
||||
});
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
base
|
||||
}
|
||||
|
||||
fn multiple_output_types_to_stdout(
|
||||
output_types: &OutputTypes,
|
||||
single_output_file_is_stdout: bool,
|
||||
|
|
|
@ -989,7 +989,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &
|
|||
impl EarlyLintPass for UnusedDocComment {
|
||||
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
|
||||
let kind = match stmt.kind {
|
||||
ast::StmtKind::Local(..) => "statements",
|
||||
ast::StmtKind::Let(..) => "statements",
|
||||
// Disabled pending discussion in #78306
|
||||
ast::StmtKind::Item(..) => return,
|
||||
// expressions will be reported by `check_expr`.
|
||||
|
|
|
@ -914,7 +914,7 @@ trait UnusedDelimLint {
|
|||
|
||||
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
|
||||
match s.kind {
|
||||
StmtKind::Local(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
|
||||
StmtKind::Let(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
|
||||
if let Some((init, els)) = local.kind.init_else_opt() {
|
||||
let ctx = match els {
|
||||
None => UnusedDelimsCtx::AssignedValue,
|
||||
|
@ -1189,7 +1189,7 @@ impl EarlyLintPass for UnusedParens {
|
|||
}
|
||||
|
||||
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
|
||||
if let StmtKind::Local(ref local) = s.kind {
|
||||
if let StmtKind::Let(ref local) = s.kind {
|
||||
self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false));
|
||||
}
|
||||
|
||||
|
|
|
@ -557,6 +557,7 @@ declare_lint! {
|
|||
/// fn main() {
|
||||
/// use foo::bar;
|
||||
/// foo::bar();
|
||||
/// bar();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
|
|
|
@ -653,7 +653,7 @@ impl<'hir> Map<'hir> {
|
|||
| Node::ForeignItem(_)
|
||||
| Node::TraitItem(_)
|
||||
| Node::ImplItem(_)
|
||||
| Node::Stmt(Stmt { kind: StmtKind::Local(_), .. }) => break,
|
||||
| Node::Stmt(Stmt { kind: StmtKind::Let(_), .. }) => break,
|
||||
Node::Expr(expr @ Expr { kind: ExprKind::If(..) | ExprKind::Match(..), .. }) => {
|
||||
return Some(expr);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
use crate::mir;
|
||||
use crate::query::TyCtxtAt;
|
||||
use crate::ty::{Ty, TyCtxt};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
macro_rules! declare_hooks {
|
||||
|
@ -70,4 +71,10 @@ declare_hooks! {
|
|||
|
||||
/// Getting a &core::panic::Location referring to a span.
|
||||
hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue<'tcx>;
|
||||
|
||||
/// Returns `true` if this def is a function-like thing that is eligible for
|
||||
/// coverage instrumentation under `-Cinstrument-coverage`.
|
||||
///
|
||||
/// (Eligible functions might nevertheless be skipped for other reasons.)
|
||||
hook is_eligible_for_coverage(key: LocalDefId) -> bool;
|
||||
}
|
||||
|
|
|
@ -2,10 +2,19 @@
|
|||
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// Used by [`CoverageKind::BlockMarker`] to mark blocks during THIR-to-MIR
|
||||
/// lowering, so that those blocks can be identified later.
|
||||
#[derive(HashStable)]
|
||||
#[encodable]
|
||||
#[debug_format = "BlockMarkerId({})"]
|
||||
pub struct BlockMarkerId {}
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// ID of a coverage counter. Values ascend from 0.
|
||||
///
|
||||
|
@ -83,6 +92,12 @@ pub enum CoverageKind {
|
|||
/// codegen.
|
||||
SpanMarker,
|
||||
|
||||
/// Marks its enclosing basic block with an ID that can be referred to by
|
||||
/// side data in [`BranchInfo`].
|
||||
///
|
||||
/// Has no effect during codegen.
|
||||
BlockMarker { id: BlockMarkerId },
|
||||
|
||||
/// Marks the point in MIR control flow represented by a coverage counter.
|
||||
///
|
||||
/// This is eventually lowered to `llvm.instrprof.increment` in LLVM IR.
|
||||
|
@ -107,6 +122,7 @@ impl Debug for CoverageKind {
|
|||
use CoverageKind::*;
|
||||
match self {
|
||||
SpanMarker => write!(fmt, "SpanMarker"),
|
||||
BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
|
||||
CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
|
||||
ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
|
||||
}
|
||||
|
@ -163,14 +179,18 @@ pub struct Expression {
|
|||
pub enum MappingKind {
|
||||
/// Associates a normal region of code with a counter/expression/zero.
|
||||
Code(CovTerm),
|
||||
/// Associates a branch region with separate counters for true and false.
|
||||
Branch { true_term: CovTerm, false_term: CovTerm },
|
||||
}
|
||||
|
||||
impl MappingKind {
|
||||
/// Iterator over all coverage terms in this mapping kind.
|
||||
pub fn terms(&self) -> impl Iterator<Item = CovTerm> {
|
||||
let one = |a| std::iter::once(a);
|
||||
let one = |a| std::iter::once(a).chain(None);
|
||||
let two = |a, b| std::iter::once(a).chain(Some(b));
|
||||
match *self {
|
||||
Self::Code(term) => one(term),
|
||||
Self::Branch { true_term, false_term } => two(true_term, false_term),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,6 +199,9 @@ impl MappingKind {
|
|||
pub fn map_terms(&self, map_fn: impl Fn(CovTerm) -> CovTerm) -> Self {
|
||||
match *self {
|
||||
Self::Code(term) => Self::Code(map_fn(term)),
|
||||
Self::Branch { true_term, false_term } => {
|
||||
Self::Branch { true_term: map_fn(true_term), false_term: map_fn(false_term) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,3 +225,22 @@ pub struct FunctionCoverageInfo {
|
|||
pub expressions: IndexVec<ExpressionId, Expression>,
|
||||
pub mappings: Vec<Mapping>,
|
||||
}
|
||||
|
||||
/// Branch information recorded during THIR-to-MIR lowering, and stored in MIR.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct BranchInfo {
|
||||
/// 1 more than the highest-numbered [`CoverageKind::BlockMarker`] that was
|
||||
/// injected into the MIR body. This makes it possible to allocate per-ID
|
||||
/// data structures without having to scan the entire body first.
|
||||
pub num_block_markers: usize,
|
||||
pub branch_spans: Vec<BranchSpan>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct BranchSpan {
|
||||
pub span: Span,
|
||||
pub true_marker: BlockMarkerId,
|
||||
pub false_marker: BlockMarkerId,
|
||||
}
|
||||
|
|
|
@ -403,6 +403,12 @@ pub struct Body<'tcx> {
|
|||
|
||||
pub tainted_by_errors: Option<ErrorGuaranteed>,
|
||||
|
||||
/// Branch coverage information collected during MIR building, to be used by
|
||||
/// the `InstrumentCoverage` pass.
|
||||
///
|
||||
/// Only present if branch coverage is enabled and this function is eligible.
|
||||
pub coverage_branch_info: Option<Box<coverage::BranchInfo>>,
|
||||
|
||||
/// Per-function coverage information added by the `InstrumentCoverage`
|
||||
/// pass, to be used in conjunction with the coverage statements injected
|
||||
/// into this body's blocks.
|
||||
|
@ -450,6 +456,7 @@ impl<'tcx> Body<'tcx> {
|
|||
is_polymorphic: false,
|
||||
injection_phase: None,
|
||||
tainted_by_errors,
|
||||
coverage_branch_info: None,
|
||||
function_coverage_info: None,
|
||||
};
|
||||
body.is_polymorphic = body.has_non_region_param();
|
||||
|
@ -479,6 +486,7 @@ impl<'tcx> Body<'tcx> {
|
|||
is_polymorphic: false,
|
||||
injection_phase: None,
|
||||
tainted_by_errors: None,
|
||||
coverage_branch_info: None,
|
||||
function_coverage_info: None,
|
||||
};
|
||||
body.is_polymorphic = body.has_non_region_param();
|
||||
|
|
|
@ -461,6 +461,9 @@ pub fn write_mir_intro<'tcx>(
|
|||
// Add an empty line before the first block is printed.
|
||||
writeln!(w)?;
|
||||
|
||||
if let Some(branch_info) = &body.coverage_branch_info {
|
||||
write_coverage_branch_info(branch_info, w)?;
|
||||
}
|
||||
if let Some(function_coverage_info) = &body.function_coverage_info {
|
||||
write_function_coverage_info(function_coverage_info, w)?;
|
||||
}
|
||||
|
@ -468,6 +471,25 @@ pub fn write_mir_intro<'tcx>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_coverage_branch_info(
|
||||
branch_info: &coverage::BranchInfo,
|
||||
w: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
let coverage::BranchInfo { branch_spans, .. } = branch_info;
|
||||
|
||||
for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans {
|
||||
writeln!(
|
||||
w,
|
||||
"{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
|
||||
)?;
|
||||
}
|
||||
if !branch_spans.is_empty() {
|
||||
writeln!(w)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_function_coverage_info(
|
||||
function_coverage_info: &coverage::FunctionCoverageInfo,
|
||||
w: &mut dyn io::Write,
|
||||
|
|
|
@ -405,6 +405,7 @@ TrivialTypeTraversalImpls! {
|
|||
::rustc_hir::HirId,
|
||||
::rustc_hir::MatchSource,
|
||||
::rustc_target::asm::InlineAsmRegOrRegClass,
|
||||
crate::mir::coverage::BlockMarkerId,
|
||||
crate::mir::coverage::CounterId,
|
||||
crate::mir::coverage::ExpressionId,
|
||||
crate::mir::Local,
|
||||
|
|
148
compiler/rustc_mir_build/src/build/coverageinfo.rs
Normal file
148
compiler/rustc_mir_build/src/build/coverageinfo.rs
Normal file
|
@ -0,0 +1,148 @@
|
|||
use std::assert_matches::assert_matches;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind};
|
||||
use rustc_middle::mir::{self, BasicBlock, UnOp};
|
||||
use rustc_middle::thir::{ExprId, ExprKind, Thir};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
|
||||
use crate::build::Builder;
|
||||
|
||||
pub(crate) struct BranchInfoBuilder {
|
||||
/// Maps condition expressions to their enclosing `!`, for better instrumentation.
|
||||
nots: FxHashMap<ExprId, NotInfo>,
|
||||
|
||||
num_block_markers: usize,
|
||||
branch_spans: Vec<BranchSpan>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct NotInfo {
|
||||
/// When visiting the associated expression as a branch condition, treat this
|
||||
/// enclosing `!` as the branch condition instead.
|
||||
enclosing_not: ExprId,
|
||||
/// True if the associated expression is nested within an odd number of `!`
|
||||
/// expressions relative to `enclosing_not` (inclusive of `enclosing_not`).
|
||||
is_flipped: bool,
|
||||
}
|
||||
|
||||
impl BranchInfoBuilder {
|
||||
/// Creates a new branch info builder, but only if branch coverage instrumentation
|
||||
/// is enabled and `def_id` represents a function that is eligible for coverage.
|
||||
pub(crate) fn new_if_enabled(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Self> {
|
||||
if tcx.sess.instrument_coverage_branch() && tcx.is_eligible_for_coverage(def_id) {
|
||||
Some(Self { nots: FxHashMap::default(), num_block_markers: 0, branch_spans: vec![] })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Unary `!` expressions inside an `if` condition are lowered by lowering
|
||||
/// their argument instead, and then reversing the then/else arms of that `if`.
|
||||
///
|
||||
/// That's awkward for branch coverage instrumentation, so to work around that
|
||||
/// we pre-emptively visit any affected `!` expressions, and record extra
|
||||
/// information that [`Builder::visit_coverage_branch_condition`] can use to
|
||||
/// synthesize branch instrumentation for the enclosing `!`.
|
||||
pub(crate) fn visit_unary_not(&mut self, thir: &Thir<'_>, unary_not: ExprId) {
|
||||
assert_matches!(thir[unary_not].kind, ExprKind::Unary { op: UnOp::Not, .. });
|
||||
|
||||
self.visit_with_not_info(
|
||||
thir,
|
||||
unary_not,
|
||||
// Set `is_flipped: false` for the `!` itself, so that its enclosed
|
||||
// expression will have `is_flipped: true`.
|
||||
NotInfo { enclosing_not: unary_not, is_flipped: false },
|
||||
);
|
||||
}
|
||||
|
||||
fn visit_with_not_info(&mut self, thir: &Thir<'_>, expr_id: ExprId, not_info: NotInfo) {
|
||||
match self.nots.entry(expr_id) {
|
||||
// This expression has already been marked by an enclosing `!`.
|
||||
Entry::Occupied(_) => return,
|
||||
Entry::Vacant(entry) => entry.insert(not_info),
|
||||
};
|
||||
|
||||
match thir[expr_id].kind {
|
||||
ExprKind::Unary { op: UnOp::Not, arg } => {
|
||||
// Invert the `is_flipped` flag for the contents of this `!`.
|
||||
let not_info = NotInfo { is_flipped: !not_info.is_flipped, ..not_info };
|
||||
self.visit_with_not_info(thir, arg, not_info);
|
||||
}
|
||||
ExprKind::Scope { value, .. } => self.visit_with_not_info(thir, value, not_info),
|
||||
ExprKind::Use { source } => self.visit_with_not_info(thir, source, not_info),
|
||||
// All other expressions (including `&&` and `||`) don't need any
|
||||
// special handling of their contents, so stop visiting.
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_block_marker_id(&mut self) -> BlockMarkerId {
|
||||
let id = BlockMarkerId::from_usize(self.num_block_markers);
|
||||
self.num_block_markers += 1;
|
||||
id
|
||||
}
|
||||
|
||||
pub(crate) fn into_done(self) -> Option<Box<mir::coverage::BranchInfo>> {
|
||||
let Self { nots: _, num_block_markers, branch_spans } = self;
|
||||
|
||||
if num_block_markers == 0 {
|
||||
assert!(branch_spans.is_empty());
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Box::new(mir::coverage::BranchInfo { num_block_markers, branch_spans }))
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder<'_, '_> {
|
||||
/// If branch coverage is enabled, inject marker statements into `then_block`
|
||||
/// and `else_block`, and record their IDs in the table of branch spans.
|
||||
pub(crate) fn visit_coverage_branch_condition(
|
||||
&mut self,
|
||||
mut expr_id: ExprId,
|
||||
mut then_block: BasicBlock,
|
||||
mut else_block: BasicBlock,
|
||||
) {
|
||||
// Bail out if branch coverage is not enabled for this function.
|
||||
let Some(branch_info) = self.coverage_branch_info.as_ref() else { return };
|
||||
|
||||
// If this condition expression is nested within one or more `!` expressions,
|
||||
// replace it with the enclosing `!` collected by `visit_unary_not`.
|
||||
if let Some(&NotInfo { enclosing_not, is_flipped }) = branch_info.nots.get(&expr_id) {
|
||||
expr_id = enclosing_not;
|
||||
if is_flipped {
|
||||
std::mem::swap(&mut then_block, &mut else_block);
|
||||
}
|
||||
}
|
||||
let source_info = self.source_info(self.thir[expr_id].span);
|
||||
|
||||
// Now that we have `source_info`, we can upgrade to a &mut reference.
|
||||
let branch_info = self.coverage_branch_info.as_mut().expect("upgrading & to &mut");
|
||||
|
||||
let mut inject_branch_marker = |block: BasicBlock| {
|
||||
let id = branch_info.next_block_marker_id();
|
||||
|
||||
let marker_statement = mir::Statement {
|
||||
source_info,
|
||||
kind: mir::StatementKind::Coverage(Box::new(mir::Coverage {
|
||||
kind: CoverageKind::BlockMarker { id },
|
||||
})),
|
||||
};
|
||||
self.cfg.push(block, marker_statement);
|
||||
|
||||
id
|
||||
};
|
||||
|
||||
let true_marker = inject_branch_marker(then_block);
|
||||
let false_marker = inject_branch_marker(else_block);
|
||||
|
||||
branch_info.branch_spans.push(BranchSpan {
|
||||
span: source_info.span,
|
||||
true_marker,
|
||||
false_marker,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -60,6 +60,7 @@ pub(super) fn build_custom_mir<'tcx>(
|
|||
tainted_by_errors: None,
|
||||
injection_phase: None,
|
||||
pass_count: 0,
|
||||
coverage_branch_info: None,
|
||||
function_coverage_info: None,
|
||||
};
|
||||
|
||||
|
|
|
@ -105,6 +105,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
success_block.unit()
|
||||
}
|
||||
ExprKind::Unary { op: UnOp::Not, arg } => {
|
||||
// Improve branch coverage instrumentation by noting conditions
|
||||
// nested within one or more `!` expressions.
|
||||
// (Skipped if branch coverage is not enabled.)
|
||||
if let Some(branch_info) = this.coverage_branch_info.as_mut() {
|
||||
branch_info.visit_unary_not(this.thir, expr_id);
|
||||
}
|
||||
|
||||
let local_scope = this.local_scope();
|
||||
let (success_block, failure_block) =
|
||||
this.in_if_then_scope(local_scope, expr_span, |this| {
|
||||
|
@ -149,6 +156,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let else_block = this.cfg.start_new_block();
|
||||
let term = TerminatorKind::if_(operand, then_block, else_block);
|
||||
|
||||
// Record branch coverage info for this condition.
|
||||
// (Does nothing if branch coverage is not enabled.)
|
||||
this.visit_coverage_branch_condition(expr_id, then_block, else_block);
|
||||
|
||||
let source_info = this.source_info(expr_span);
|
||||
this.cfg.terminate(block, source_info, term);
|
||||
this.break_for_else(else_block, source_info);
|
||||
|
|
|
@ -234,6 +234,10 @@ struct Builder<'a, 'tcx> {
|
|||
// the root (most of them do) and saves us from retracing many sub-paths
|
||||
// many times, and rechecking many nodes.
|
||||
lint_level_roots_cache: GrowableBitSet<hir::ItemLocalId>,
|
||||
|
||||
/// Collects additional coverage information during MIR building.
|
||||
/// Only present if branch coverage is enabled and this function is eligible.
|
||||
coverage_branch_info: Option<coverageinfo::BranchInfoBuilder>,
|
||||
}
|
||||
|
||||
type CaptureMap<'tcx> = SortedIndexMultiMap<usize, hir::HirId, Capture<'tcx>>;
|
||||
|
@ -807,6 +811,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
unit_temp: None,
|
||||
var_debug_info: vec![],
|
||||
lint_level_roots_cache: GrowableBitSet::new_empty(),
|
||||
coverage_branch_info: coverageinfo::BranchInfoBuilder::new_if_enabled(tcx, def),
|
||||
};
|
||||
|
||||
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
|
||||
|
@ -826,7 +831,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
Body::new(
|
||||
let mut body = Body::new(
|
||||
MirSource::item(self.def_id.to_def_id()),
|
||||
self.cfg.basic_blocks,
|
||||
self.source_scopes,
|
||||
|
@ -837,7 +842,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
self.fn_span,
|
||||
self.coroutine,
|
||||
None,
|
||||
)
|
||||
);
|
||||
body.coverage_branch_info = self.coverage_branch_info.and_then(|b| b.into_done());
|
||||
body
|
||||
}
|
||||
|
||||
fn insert_upvar_arg(&mut self) {
|
||||
|
@ -1111,6 +1118,7 @@ pub(crate) fn parse_float_into_scalar(
|
|||
|
||||
mod block;
|
||||
mod cfg;
|
||||
mod coverageinfo;
|
||||
mod custom;
|
||||
mod expr;
|
||||
mod matches;
|
||||
|
|
|
@ -63,7 +63,7 @@ impl<'tcx> Cx<'tcx> {
|
|||
// ignore for purposes of the MIR
|
||||
None
|
||||
}
|
||||
hir::StmtKind::Local(local) => {
|
||||
hir::StmtKind::Let(local) => {
|
||||
let remainder_scope = region::Scope {
|
||||
id: block_id,
|
||||
data: region::ScopeData::Remainder(region::FirstStatementIndex::new(
|
||||
|
|
|
@ -14,7 +14,6 @@ use self::spans::{BcbMapping, BcbMappingKind, CoverageSpans};
|
|||
use crate::MirPass;
|
||||
|
||||
use rustc_middle::hir;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::coverage::*;
|
||||
use rustc_middle::mir::{
|
||||
self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator,
|
||||
|
@ -44,7 +43,7 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
|
|||
|
||||
let def_id = mir_source.def_id().expect_local();
|
||||
|
||||
if !is_eligible_for_coverage(tcx, def_id) {
|
||||
if !tcx.is_eligible_for_coverage(def_id) {
|
||||
trace!("InstrumentCoverage skipped for {def_id:?} (not eligible)");
|
||||
return;
|
||||
}
|
||||
|
@ -140,6 +139,10 @@ fn create_mappings<'tcx>(
|
|||
.filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| {
|
||||
let kind = match bcb_mapping_kind {
|
||||
BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)),
|
||||
BcbMappingKind::Branch { true_bcb, false_bcb } => MappingKind::Branch {
|
||||
true_term: term_for_bcb(true_bcb),
|
||||
false_term: term_for_bcb(false_bcb),
|
||||
},
|
||||
};
|
||||
let code_region = make_code_region(source_map, file_name, span, body_span)?;
|
||||
Some(Mapping { kind, code_region })
|
||||
|
@ -349,37 +352,6 @@ fn check_code_region(code_region: CodeRegion) -> Option<CodeRegion> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
// Only instrument functions, methods, and closures (not constants since they are evaluated
|
||||
// at compile time by Miri).
|
||||
// FIXME(#73156): Handle source code coverage in const eval, but note, if and when const
|
||||
// expressions get coverage spans, we will probably have to "carve out" space for const
|
||||
// expressions from coverage spans in enclosing MIR's, like we do for closures. (That might
|
||||
// be tricky if const expressions have no corresponding statements in the enclosing MIR.
|
||||
// Closures are carved out by their initial `Assign` statement.)
|
||||
if !tcx.def_kind(def_id).is_fn_like() {
|
||||
trace!("InstrumentCoverage skipped for {def_id:?} (not an fn-like)");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't instrument functions with `#[automatically_derived]` on their
|
||||
// enclosing impl block, on the assumption that most users won't care about
|
||||
// coverage for derived impls.
|
||||
if let Some(impl_of) = tcx.impl_of_method(def_id.to_def_id())
|
||||
&& tcx.is_automatically_derived(impl_of)
|
||||
{
|
||||
trace!("InstrumentCoverage skipped for {def_id:?} (automatically derived)");
|
||||
return false;
|
||||
}
|
||||
|
||||
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
|
||||
trace!("InstrumentCoverage skipped for {def_id:?} (`#[coverage(off)]`)");
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Function information extracted from HIR by the coverage instrumentor.
|
||||
#[derive(Debug)]
|
||||
struct ExtractedHirInfo {
|
||||
|
|
|
@ -1,14 +1,49 @@
|
|||
use super::*;
|
||||
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_middle::mir::coverage::*;
|
||||
use rustc_middle::mir::{Body, CoverageIdsInfo};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::{self};
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::coverage::{CounterId, CoverageKind};
|
||||
use rustc_middle::mir::{Body, Coverage, CoverageIdsInfo, Statement, StatementKind};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
|
||||
/// A `query` provider for retrieving coverage information injected into MIR.
|
||||
/// Registers query/hook implementations related to coverage.
|
||||
pub(crate) fn provide(providers: &mut Providers) {
|
||||
providers.coverage_ids_info = |tcx, def_id| coverage_ids_info(tcx, def_id);
|
||||
providers.hooks.is_eligible_for_coverage =
|
||||
|TyCtxtAt { tcx, .. }, def_id| is_eligible_for_coverage(tcx, def_id);
|
||||
providers.queries.coverage_ids_info = coverage_ids_info;
|
||||
}
|
||||
|
||||
/// Hook implementation for [`TyCtxt::is_eligible_for_coverage`].
|
||||
fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
// Only instrument functions, methods, and closures (not constants since they are evaluated
|
||||
// at compile time by Miri).
|
||||
// FIXME(#73156): Handle source code coverage in const eval, but note, if and when const
|
||||
// expressions get coverage spans, we will probably have to "carve out" space for const
|
||||
// expressions from coverage spans in enclosing MIR's, like we do for closures. (That might
|
||||
// be tricky if const expressions have no corresponding statements in the enclosing MIR.
|
||||
// Closures are carved out by their initial `Assign` statement.)
|
||||
if !tcx.def_kind(def_id).is_fn_like() {
|
||||
trace!("InstrumentCoverage skipped for {def_id:?} (not an fn-like)");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't instrument functions with `#[automatically_derived]` on their
|
||||
// enclosing impl block, on the assumption that most users won't care about
|
||||
// coverage for derived impls.
|
||||
if let Some(impl_of) = tcx.impl_of_method(def_id.to_def_id())
|
||||
&& tcx.is_automatically_derived(impl_of)
|
||||
{
|
||||
trace!("InstrumentCoverage skipped for {def_id:?} (automatically derived)");
|
||||
return false;
|
||||
}
|
||||
|
||||
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
|
||||
trace!("InstrumentCoverage skipped for {def_id:?} (`#[coverage(off)]`)");
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Query implementation for `coverage_ids_info`.
|
||||
|
|
|
@ -13,6 +13,8 @@ mod from_mir;
|
|||
pub(super) enum BcbMappingKind {
|
||||
/// Associates an ordinary executable code span with its corresponding BCB.
|
||||
Code(BasicCoverageBlock),
|
||||
/// Associates a branch span with BCBs for its true and false arms.
|
||||
Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -66,6 +68,12 @@ pub(super) fn generate_coverage_spans(
|
|||
// Each span produced by the generator represents an ordinary code region.
|
||||
BcbMapping { kind: BcbMappingKind::Code(bcb), span }
|
||||
}));
|
||||
|
||||
mappings.extend(from_mir::extract_branch_mappings(
|
||||
mir_body,
|
||||
hir_info.body_span,
|
||||
basic_coverage_blocks,
|
||||
));
|
||||
}
|
||||
|
||||
if mappings.is_empty() {
|
||||
|
@ -80,6 +88,10 @@ pub(super) fn generate_coverage_spans(
|
|||
for &BcbMapping { kind, span: _ } in &mappings {
|
||||
match kind {
|
||||
BcbMappingKind::Code(bcb) => insert(bcb),
|
||||
BcbMappingKind::Branch { true_bcb, false_bcb } => {
|
||||
insert(true_bcb);
|
||||
insert(false_bcb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind};
|
||||
use rustc_middle::mir::{
|
||||
self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
|
||||
self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
|
||||
TerminatorKind,
|
||||
};
|
||||
use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
|
||||
|
@ -9,6 +11,7 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
|
|||
use crate::coverage::graph::{
|
||||
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
|
||||
};
|
||||
use crate::coverage::spans::{BcbMapping, BcbMappingKind};
|
||||
use crate::coverage::ExtractedHirInfo;
|
||||
|
||||
/// Traverses the MIR body to produce an initial collection of coverage-relevant
|
||||
|
@ -179,8 +182,6 @@ fn is_closure_like(statement: &Statement<'_>) -> bool {
|
|||
/// If the MIR `Statement` has a span contributive to computing coverage spans,
|
||||
/// return it; otherwise return `None`.
|
||||
fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
|
||||
use mir::coverage::CoverageKind;
|
||||
|
||||
match statement.kind {
|
||||
// These statements have spans that are often outside the scope of the executed source code
|
||||
// for their parent `BasicBlock`.
|
||||
|
@ -225,6 +226,11 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
|
|||
Some(statement.source_info.span)
|
||||
}
|
||||
|
||||
StatementKind::Coverage(box mir::Coverage {
|
||||
// Block markers are used for branch coverage, so ignore them here.
|
||||
kind: CoverageKind::BlockMarker {..}
|
||||
}) => None,
|
||||
|
||||
StatementKind::Coverage(box mir::Coverage {
|
||||
// These coverage statements should not exist prior to coverage instrumentation.
|
||||
kind: CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. }
|
||||
|
@ -358,3 +364,51 @@ impl SpanFromMir {
|
|||
Self { span, visible_macro, bcb, is_hole }
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn extract_branch_mappings(
|
||||
mir_body: &mir::Body<'_>,
|
||||
body_span: Span,
|
||||
basic_coverage_blocks: &CoverageGraph,
|
||||
) -> Vec<BcbMapping> {
|
||||
let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else {
|
||||
return vec![];
|
||||
};
|
||||
|
||||
let mut block_markers = IndexVec::<BlockMarkerId, Option<BasicBlock>>::from_elem_n(
|
||||
None,
|
||||
branch_info.num_block_markers,
|
||||
);
|
||||
|
||||
// Fill out the mapping from block marker IDs to their enclosing blocks.
|
||||
for (bb, data) in mir_body.basic_blocks.iter_enumerated() {
|
||||
for statement in &data.statements {
|
||||
if let StatementKind::Coverage(coverage) = &statement.kind
|
||||
&& let CoverageKind::BlockMarker { id } = coverage.kind
|
||||
{
|
||||
block_markers[id] = Some(bb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
branch_info
|
||||
.branch_spans
|
||||
.iter()
|
||||
.filter_map(|&BranchSpan { span: raw_span, true_marker, false_marker }| {
|
||||
// For now, ignore any branch span that was introduced by
|
||||
// expansion. This makes things like assert macros less noisy.
|
||||
if !raw_span.ctxt().outer_expn_data().is_root() {
|
||||
return None;
|
||||
}
|
||||
let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?;
|
||||
|
||||
let bcb_from_marker = |marker: BlockMarkerId| {
|
||||
Some(basic_coverage_blocks.bcb_from_bb(block_markers[marker]?)?)
|
||||
};
|
||||
|
||||
let true_bcb = bcb_from_marker(true_marker)?;
|
||||
let false_bcb = bcb_from_marker(false_marker)?;
|
||||
|
||||
Some(BcbMapping { kind: BcbMappingKind::Branch { true_bcb, false_bcb }, span })
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
|
|
@ -37,8 +37,9 @@ use rustc_middle::mir::{
|
|||
LocalDecl, MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue,
|
||||
SourceInfo, Statement, StatementKind, TerminatorKind, START_BLOCK,
|
||||
};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::query;
|
||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_span::{source_map::Spanned, sym, DUMMY_SP};
|
||||
use rustc_trait_selection::traits;
|
||||
|
||||
|
@ -124,7 +125,7 @@ pub fn provide(providers: &mut Providers) {
|
|||
ffi_unwind_calls::provide(providers);
|
||||
shim::provide(providers);
|
||||
cross_crate_inline::provide(providers);
|
||||
*providers = Providers {
|
||||
providers.queries = query::Providers {
|
||||
mir_keys,
|
||||
mir_const,
|
||||
mir_const_qualif,
|
||||
|
@ -139,7 +140,7 @@ pub fn provide(providers: &mut Providers) {
|
|||
mir_inliner_callees: inline::cycle::mir_inliner_callees,
|
||||
promoted_mir,
|
||||
deduced_param_attrs: deduce_param_attrs::deduced_param_attrs,
|
||||
..*providers
|
||||
..providers.queries
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -254,7 +254,7 @@ impl<'a> Parser<'a> {
|
|||
let local = this.parse_local(attrs)?;
|
||||
// FIXME - maybe capture semicolon in recovery?
|
||||
Ok((
|
||||
this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)),
|
||||
this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
|
||||
TrailingToken::None,
|
||||
))
|
||||
})?;
|
||||
|
@ -278,7 +278,7 @@ impl<'a> Parser<'a> {
|
|||
} else {
|
||||
TrailingToken::None
|
||||
};
|
||||
Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)), trailing))
|
||||
Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), trailing))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -764,7 +764,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
|
||||
StmtKind::Local(local) if let Err(mut e) = self.expect_semi() => {
|
||||
StmtKind::Let(local) if let Err(mut e) = self.expect_semi() => {
|
||||
// We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
|
||||
match &mut local.kind {
|
||||
LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
|
||||
|
@ -820,7 +820,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
eat_semi = false;
|
||||
}
|
||||
StmtKind::Empty | StmtKind::Item(_) | StmtKind::Local(_) | StmtKind::Semi(_) => {
|
||||
StmtKind::Empty | StmtKind::Item(_) | StmtKind::Let(_) | StmtKind::Semi(_) => {
|
||||
eat_semi = false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2444,7 +2444,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
|
|||
|
||||
fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
|
||||
// When checking statements ignore expressions, they will be checked later.
|
||||
if let hir::StmtKind::Local(l) = stmt.kind {
|
||||
if let hir::StmtKind::Let(l) = stmt.kind {
|
||||
self.check_attributes(l.hir_id, stmt.span, Target::Statement, None);
|
||||
}
|
||||
intravisit::walk_stmt(self, stmt)
|
||||
|
|
|
@ -277,7 +277,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
|
|||
fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
|
||||
record_variants!(
|
||||
(self, s, s.kind, Id::Node(s.hir_id), hir, Stmt, StmtKind),
|
||||
[Local, Item, Expr, Semi]
|
||||
[Let, Item, Expr, Semi]
|
||||
);
|
||||
hir_visit::walk_stmt(self, s)
|
||||
}
|
||||
|
@ -539,7 +539,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
|
|||
fn visit_stmt(&mut self, s: &'v ast::Stmt) {
|
||||
record_variants!(
|
||||
(self, s, s.kind, Id::None, ast, Stmt, StmtKind),
|
||||
[Local, Item, Expr, Semi, Empty, MacCall]
|
||||
[Let, Item, Expr, Semi, Empty, MacCall]
|
||||
);
|
||||
ast_visit::walk_stmt(self, s)
|
||||
}
|
||||
|
|
|
@ -771,7 +771,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||
|
||||
fn propagate_through_stmt(&mut self, stmt: &hir::Stmt<'_>, succ: LiveNode) -> LiveNode {
|
||||
match stmt.kind {
|
||||
hir::StmtKind::Local(local) => {
|
||||
hir::StmtKind::Let(local) => {
|
||||
// Note: we mark the variable as defined regardless of whether
|
||||
// there is an initializer. Initially I had thought to only mark
|
||||
// the live variable as defined if it was initialized, and then we
|
||||
|
|
|
@ -280,7 +280,7 @@ impl<'tcx> Visitor<'tcx> for CheckInlineAssembly<'tcx> {
|
|||
fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
|
||||
match stmt.kind {
|
||||
StmtKind::Item(..) => {}
|
||||
StmtKind::Local(..) => {
|
||||
StmtKind::Let(..) => {
|
||||
self.items.push((ItemKind::NonAsm, stmt.span));
|
||||
}
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
|
||||
|
|
|
@ -23,18 +23,19 @@
|
|||
// - `check_unused` finally emits the diagnostics based on the data generated
|
||||
// in the last step
|
||||
|
||||
use crate::imports::ImportKind;
|
||||
use crate::imports::{Import, ImportKind};
|
||||
use crate::module_to_string;
|
||||
use crate::Resolver;
|
||||
|
||||
use crate::NameBindingKind;
|
||||
use crate::{LexicalScopeBinding, NameBindingKind};
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::visit::{self, Visitor};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_errors::{pluralize, MultiSpan};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES, UNUSED_IMPORTS};
|
||||
use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES};
|
||||
use rustc_session::lint::builtin::{UNUSED_IMPORTS, UNUSED_QUALIFICATIONS};
|
||||
use rustc_session::lint::BuiltinLintDiag;
|
||||
use rustc_span::symbol::{kw, Ident};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
@ -514,8 +515,59 @@ impl Resolver<'_, '_> {
|
|||
}
|
||||
}
|
||||
|
||||
let mut redundant_imports = UnordSet::default();
|
||||
for import in check_redundant_imports {
|
||||
self.check_for_redundant_imports(import);
|
||||
if self.check_for_redundant_imports(import)
|
||||
&& let Some(id) = import.id()
|
||||
{
|
||||
redundant_imports.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
// The lint fixes for unused_import and unnecessary_qualification may conflict.
|
||||
// Deleting both unused imports and unnecessary segments of an item may result
|
||||
// in the item not being found.
|
||||
for unn_qua in &self.potentially_unnecessary_qualifications {
|
||||
if let LexicalScopeBinding::Item(name_binding) = unn_qua.binding
|
||||
&& let NameBindingKind::Import { import, .. } = name_binding.kind
|
||||
&& (is_unused_import(import, &unused_imports)
|
||||
|| is_redundant_import(import, &redundant_imports))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
self.lint_buffer.buffer_lint_with_diagnostic(
|
||||
UNUSED_QUALIFICATIONS,
|
||||
unn_qua.node_id,
|
||||
unn_qua.path_span,
|
||||
"unnecessary qualification",
|
||||
BuiltinLintDiag::UnusedQualifications { removal_span: unn_qua.removal_span },
|
||||
);
|
||||
}
|
||||
|
||||
fn is_redundant_import(
|
||||
import: Import<'_>,
|
||||
redundant_imports: &UnordSet<ast::NodeId>,
|
||||
) -> bool {
|
||||
if let Some(id) = import.id()
|
||||
&& redundant_imports.contains(&id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_unused_import(
|
||||
import: Import<'_>,
|
||||
unused_imports: &FxIndexMap<ast::NodeId, UnusedImport>,
|
||||
) -> bool {
|
||||
if let Some(unused_import) = unused_imports.get(&import.root_id)
|
||||
&& let Some(id) = import.id()
|
||||
&& unused_import.unused.contains(&id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1306,7 +1306,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
None
|
||||
}
|
||||
|
||||
pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'a>) {
|
||||
pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'a>) -> bool {
|
||||
// This function is only called for single imports.
|
||||
let ImportKind::Single {
|
||||
source, target, ref source_bindings, ref target_bindings, id, ..
|
||||
|
@ -1317,12 +1317,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
|
||||
// Skip if the import is of the form `use source as target` and source != target.
|
||||
if source != target {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip if the import was produced by a macro.
|
||||
if import.parent_scope.expansion != LocalExpnId::ROOT {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip if we are inside a named module (in contrast to an anonymous
|
||||
|
@ -1332,7 +1332,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
if import.used.get() == Some(Used::Other)
|
||||
|| self.effective_visibilities.is_exported(self.local_def_id(id))
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut is_redundant = true;
|
||||
|
@ -1375,7 +1375,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
format!("the item `{source}` is imported redundantly"),
|
||||
BuiltinLintDiag::RedundantImport(redundant_spans, source),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn resolve_glob_import(&mut self, import: Import<'a>) {
|
||||
|
|
|
@ -580,6 +580,15 @@ impl MaybeExported<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Used for recording UnnecessaryQualification.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct UnnecessaryQualification<'a> {
|
||||
pub binding: LexicalScopeBinding<'a>,
|
||||
pub node_id: NodeId,
|
||||
pub path_span: Span,
|
||||
pub removal_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DiagMetadata<'ast> {
|
||||
/// The current trait's associated items' ident, used for diagnostic suggestions.
|
||||
|
@ -4654,20 +4663,16 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
|||
let ns = if i + 1 == path.len() { ns } else { TypeNS };
|
||||
let res = self.r.partial_res_map.get(&seg.id?)?.full_res()?;
|
||||
let binding = self.resolve_ident_in_lexical_scope(seg.ident, ns, None, None)?;
|
||||
|
||||
(res == binding.res()).then_some(seg)
|
||||
(res == binding.res()).then_some((seg, binding))
|
||||
});
|
||||
|
||||
if let Some(unqualified) = unqualified {
|
||||
self.r.lint_buffer.buffer_lint_with_diagnostic(
|
||||
lint::builtin::UNUSED_QUALIFICATIONS,
|
||||
finalize.node_id,
|
||||
finalize.path_span,
|
||||
"unnecessary qualification",
|
||||
lint::BuiltinLintDiag::UnusedQualifications {
|
||||
removal_span: path[0].ident.span.until(unqualified.ident.span),
|
||||
},
|
||||
);
|
||||
if let Some((seg, binding)) = unqualified {
|
||||
self.r.potentially_unnecessary_qualifications.push(UnnecessaryQualification {
|
||||
binding,
|
||||
node_id: finalize.node_id,
|
||||
path_span: finalize.path_span,
|
||||
removal_span: path[0].ident.span.until(seg.ident.span),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ use std::fmt;
|
|||
|
||||
use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion};
|
||||
use imports::{Import, ImportData, ImportKind, NameResolution};
|
||||
use late::{HasGenericParams, PathSource, PatternSource};
|
||||
use late::{HasGenericParams, PathSource, PatternSource, UnnecessaryQualification};
|
||||
use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
|
||||
|
||||
use crate::effective_visibilities::EffectiveVisibilitiesVisitor;
|
||||
|
@ -372,7 +372,7 @@ impl<'a> From<&'a ast::PathSegment> for Segment {
|
|||
/// This refers to the thing referred by a name. The difference between `Res` and `Item` is that
|
||||
/// items are visible in their whole block, while `Res`es only from the place they are defined
|
||||
/// forward.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum LexicalScopeBinding<'a> {
|
||||
Item(NameBinding<'a>),
|
||||
Res(Res),
|
||||
|
@ -1105,6 +1105,8 @@ pub struct Resolver<'a, 'tcx> {
|
|||
|
||||
potentially_unused_imports: Vec<Import<'a>>,
|
||||
|
||||
potentially_unnecessary_qualifications: Vec<UnnecessaryQualification<'a>>,
|
||||
|
||||
/// Table for mapping struct IDs into struct constructor IDs,
|
||||
/// it's not used during normal resolution, only for better error reporting.
|
||||
/// Also includes of list of each fields visibility
|
||||
|
@ -1464,6 +1466,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
local_macro_def_scopes: FxHashMap::default(),
|
||||
name_already_seen: FxHashMap::default(),
|
||||
potentially_unused_imports: Vec::new(),
|
||||
potentially_unnecessary_qualifications: Default::default(),
|
||||
struct_constructors: Default::default(),
|
||||
unused_macros: Default::default(),
|
||||
unused_macro_rules: Default::default(),
|
||||
|
|
|
@ -111,4 +111,7 @@ session_unleashed_feature_help_unnamed = skipping check that does not even have
|
|||
|
||||
session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto`
|
||||
|
||||
session_unsupported_crate_type_for_target =
|
||||
dropping unsupported crate type `{$crate_type}` for target `{$target_triple}`
|
||||
|
||||
session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is greater than 5
|
||||
|
|
|
@ -146,7 +146,7 @@ pub enum InstrumentCoverage {
|
|||
/// Individual flag values controlled by `-Z coverage-options`.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct CoverageOptions {
|
||||
/// Add branch coverage instrumentation (placeholder flag; not yet implemented).
|
||||
/// Add branch coverage instrumentation.
|
||||
pub branch: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use rustc_macros::Diagnostic;
|
|||
use rustc_span::{Span, Symbol};
|
||||
use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple};
|
||||
|
||||
use crate::parse::ParseSess;
|
||||
use crate::{config::CrateType, parse::ParseSess};
|
||||
|
||||
pub struct FeatureGateError {
|
||||
pub span: MultiSpan,
|
||||
|
@ -345,6 +345,13 @@ pub(crate) struct BinaryFloatLiteralNotSupported {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(session_unsupported_crate_type_for_target)]
|
||||
pub struct UnsupportedCrateTypeForTarget<'a> {
|
||||
pub crate_type: CrateType,
|
||||
pub target_triple: &'a TargetTriple,
|
||||
}
|
||||
|
||||
pub fn report_lit_error(
|
||||
psess: &ParseSess,
|
||||
err: LitError,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Related to out filenames of compilation (e.g. binaries).
|
||||
use crate::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
|
||||
use crate::config::{self, CrateType, Input, OutFileName, OutputFilenames, OutputType};
|
||||
use crate::errors::{
|
||||
CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable,
|
||||
self, CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable,
|
||||
InvalidCharacterInCrateName, InvalidCrateNameHelp,
|
||||
};
|
||||
use crate::Session;
|
||||
|
@ -200,3 +200,64 @@ pub fn invalid_output_for_target(sess: &Session, crate_type: CrateType) -> bool
|
|||
|
||||
false
|
||||
}
|
||||
|
||||
pub const CRATE_TYPES: &[(Symbol, CrateType)] = &[
|
||||
(sym::rlib, CrateType::Rlib),
|
||||
(sym::dylib, CrateType::Dylib),
|
||||
(sym::cdylib, CrateType::Cdylib),
|
||||
(sym::lib, config::default_lib_output()),
|
||||
(sym::staticlib, CrateType::Staticlib),
|
||||
(sym::proc_dash_macro, CrateType::ProcMacro),
|
||||
(sym::bin, CrateType::Executable),
|
||||
];
|
||||
|
||||
pub fn categorize_crate_type(s: Symbol) -> Option<CrateType> {
|
||||
Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1)
|
||||
}
|
||||
|
||||
pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<CrateType> {
|
||||
// If we're generating a test executable, then ignore all other output
|
||||
// styles at all other locations
|
||||
if session.opts.test {
|
||||
return vec![CrateType::Executable];
|
||||
}
|
||||
|
||||
// Only check command line flags if present. If no types are specified by
|
||||
// command line, then reuse the empty `base` Vec to hold the types that
|
||||
// will be found in crate attributes.
|
||||
// JUSTIFICATION: before wrapper fn is available
|
||||
#[allow(rustc::bad_opt_access)]
|
||||
let mut base = session.opts.crate_types.clone();
|
||||
if base.is_empty() {
|
||||
let attr_types = attrs.iter().filter_map(|a| {
|
||||
if a.has_name(sym::crate_type)
|
||||
&& let Some(s) = a.value_str()
|
||||
{
|
||||
categorize_crate_type(s)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
base.extend(attr_types);
|
||||
if base.is_empty() {
|
||||
base.push(default_output_for_target(session));
|
||||
} else {
|
||||
base.sort();
|
||||
base.dedup();
|
||||
}
|
||||
}
|
||||
|
||||
base.retain(|crate_type| {
|
||||
if invalid_output_for_target(session, *crate_type) {
|
||||
session.dcx().emit_warn(errors::UnsupportedCrateTypeForTarget {
|
||||
crate_type: *crate_type,
|
||||
target_triple: &session.opts.target_triple,
|
||||
});
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
base
|
||||
}
|
||||
|
|
|
@ -633,10 +633,8 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
|
|||
/// If the resulting alignment differs from the type's alignment,
|
||||
/// the argument will be copied to an alloca with sufficient alignment,
|
||||
/// either in the caller (if the type's alignment is lower than the byval alignment)
|
||||
/// or in the callee† (if the type's alignment is higher than the byval alignment),
|
||||
/// or in the callee (if the type's alignment is higher than the byval alignment),
|
||||
/// to ensure that Rust code never sees an underaligned pointer.
|
||||
///
|
||||
/// † This is currently broken, see <https://github.com/rust-lang/rust/pull/122212>.
|
||||
pub fn make_indirect_byval(&mut self, byval_align: Option<Align>) {
|
||||
assert!(!self.layout.is_unsized(), "used byval ABI for unsized layout");
|
||||
self.make_indirect();
|
||||
|
|
|
@ -763,7 +763,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
|
||||
let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?);
|
||||
match self.tcx.parent_hir_node(hir_id) {
|
||||
hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. }) => {
|
||||
hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Let(local), .. }) => {
|
||||
get_name(err, &local.pat.kind)
|
||||
}
|
||||
// Different to previous arm because one is `&hir::Local` and the other
|
||||
|
|
|
@ -856,6 +856,48 @@ impl Duration {
|
|||
(self.secs as f32) + (self.nanos.0 as f32) / (NANOS_PER_SEC as f32)
|
||||
}
|
||||
|
||||
/// Returns the number of milliseconds contained by this `Duration` as `f64`.
|
||||
///
|
||||
/// The returned value does include the fractional (nanosecond) part of the duration.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// #![feature(duration_millis_float)]
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let dur = Duration::new(2, 345_678_000);
|
||||
/// assert_eq!(dur.as_millis_f64(), 2345.678);
|
||||
/// ```
|
||||
#[unstable(feature = "duration_millis_float", issue = "122451")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")]
|
||||
pub const fn as_millis_f64(&self) -> f64 {
|
||||
(self.secs as f64) * (MILLIS_PER_SEC as f64)
|
||||
+ (self.nanos.0 as f64) / (NANOS_PER_MILLI as f64)
|
||||
}
|
||||
|
||||
/// Returns the number of milliseconds contained by this `Duration` as `f32`.
|
||||
///
|
||||
/// The returned value does include the fractional (nanosecond) part of the duration.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// #![feature(duration_millis_float)]
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let dur = Duration::new(2, 345_678_000);
|
||||
/// assert_eq!(dur.as_millis_f32(), 2345.678);
|
||||
/// ```
|
||||
#[unstable(feature = "duration_millis_float", issue = "122451")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")]
|
||||
pub const fn as_millis_f32(&self) -> f32 {
|
||||
(self.secs as f32) * (MILLIS_PER_SEC as f32)
|
||||
+ (self.nanos.0 as f32) / (NANOS_PER_MILLI as f32)
|
||||
}
|
||||
|
||||
/// Creates a new `Duration` from the specified number of seconds represented
|
||||
/// as `f64`.
|
||||
///
|
||||
|
|
|
@ -13,4 +13,4 @@ core = { path = "../core" }
|
|||
compiler_builtins = { version = "0.1.0", features = ['rustc-dep-of-std'] }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.69"
|
||||
cc = "1.0.90"
|
||||
|
|
|
@ -39,6 +39,13 @@ pub trait CommandExt: Sealed {
|
|||
/// Sets the child process's user ID. This translates to a
|
||||
/// `setuid` call in the child process. Failure in the `setuid`
|
||||
/// call will cause the spawn to fail.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// This will also trigger a call to `setgroups(0, NULL)` in the child
|
||||
/// process if no groups have been specified.
|
||||
/// This removes supplementary groups that might have given the child
|
||||
/// unwanted permissions.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn uid(&mut self, id: UserId) -> &mut process::Command;
|
||||
|
||||
|
|
|
@ -330,14 +330,22 @@ impl Command {
|
|||
if let Some(u) = self.get_uid() {
|
||||
// When dropping privileges from root, the `setgroups` call
|
||||
// will remove any extraneous groups. We only drop groups
|
||||
// if the current uid is 0 and we weren't given an explicit
|
||||
// if we have CAP_SETGID and we weren't given an explicit
|
||||
// set of groups. If we don't call this, then even though our
|
||||
// uid has dropped, we may still have groups that enable us to
|
||||
// do super-user things.
|
||||
//FIXME: Redox kernel does not support setgroups yet
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
if libc::getuid() == 0 && self.get_groups().is_none() {
|
||||
cvt(libc::setgroups(0, crate::ptr::null()))?;
|
||||
if self.get_groups().is_none() {
|
||||
let res = cvt(libc::setgroups(0, crate::ptr::null()));
|
||||
if let Err(e) = res {
|
||||
// Here we ignore the case of not having CAP_SETGID.
|
||||
// An alternative would be to require CAP_SETGID (in
|
||||
// addition to CAP_SETUID) for setting the UID.
|
||||
if e.raw_os_error() != Some(libc::EPERM) {
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
cvt(libc::setuid(u as uid_t))?;
|
||||
}
|
||||
|
|
|
@ -352,7 +352,7 @@ This unstable option provides finer control over some aspects of coverage
|
|||
instrumentation. Pass one or more of the following values, separated by commas.
|
||||
|
||||
- `branch` or `no-branch`
|
||||
- Placeholder for potential branch coverage support in the future.
|
||||
- Enables or disables branch coverage instrumentation.
|
||||
|
||||
## Other references
|
||||
|
||||
|
|
|
@ -5,4 +5,4 @@ This option controls details of the coverage instrumentation performed by
|
|||
|
||||
Multiple options can be passed, separated by commas. Valid options are:
|
||||
|
||||
- `branch` or `no-branch`: Placeholder for future branch coverage support.
|
||||
- `branch` or `no-branch`: Enables or disables branch coverage instrumentation.
|
||||
|
|
|
@ -52,7 +52,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
|
|||
.as_ref()
|
||||
.map_or(false, |e| is_relevant_expr(cx, typeck_results, e)),
|
||||
|stmt| match &stmt.kind {
|
||||
StmtKind::Local(_) => true,
|
||||
StmtKind::Let(_) => true,
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
|
||||
StmtKind::Item(_) => false,
|
||||
},
|
||||
|
|
|
@ -349,7 +349,7 @@ impl BlockEq {
|
|||
|
||||
/// If the statement is a local, checks if the bound names match the expected list of names.
|
||||
fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
|
||||
if let StmtKind::Local(l) = s.kind {
|
||||
if let StmtKind::Let(l) = s.kind {
|
||||
let mut i = 0usize;
|
||||
let mut res = true;
|
||||
l.pat.each_binding_or_first(&mut |_, _, _, name| {
|
||||
|
@ -389,7 +389,7 @@ fn eq_stmts(
|
|||
eq: &mut HirEqInterExpr<'_, '_, '_>,
|
||||
moved_bindings: &mut Vec<(HirId, Symbol)>,
|
||||
) -> bool {
|
||||
(if let StmtKind::Local(l) = stmt.kind {
|
||||
(if let StmtKind::Let(l) = stmt.kind {
|
||||
let old_count = moved_bindings.len();
|
||||
l.pat.each_binding_or_first(&mut |_, id, _, name| {
|
||||
moved_bindings.push((id, name.name));
|
||||
|
@ -432,7 +432,7 @@ fn scan_block_for_eq<'tcx>(
|
|||
.iter()
|
||||
.enumerate()
|
||||
.find(|&(i, stmt)| {
|
||||
if let StmtKind::Local(l) = stmt.kind
|
||||
if let StmtKind::Let(l) = stmt.kind
|
||||
&& needs_ordered_drop(cx, cx.typeck_results().node_type(l.hir_id))
|
||||
{
|
||||
local_needs_ordered_drop = true;
|
||||
|
@ -509,7 +509,7 @@ fn scan_block_for_eq<'tcx>(
|
|||
// Clear out all locals seen at the end so far. None of them can be moved.
|
||||
let stmts = &blocks[0].stmts;
|
||||
for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] {
|
||||
if let StmtKind::Local(l) = stmt.kind {
|
||||
if let StmtKind::Let(l) = stmt.kind {
|
||||
l.pat.each_binding_or_first(&mut |_, id, _, _| {
|
||||
// FIXME(rust/#120456) - is `swap_remove` correct?
|
||||
eq.locals.swap_remove(&id);
|
||||
|
|
|
@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the
|
||||
// `default` method of the `Default` trait, and store statement index in current block being
|
||||
// checked and the name of the bound variable
|
||||
let (local, variant, binding_name, binding_type, span) = if let StmtKind::Local(local) = stmt.kind
|
||||
let (local, variant, binding_name, binding_type, span) = if let StmtKind::Let(local) = stmt.kind
|
||||
// only take `let ...` statements
|
||||
&& let Some(expr) = local.init
|
||||
&& !any_parent_is_automatically_derived(cx.tcx, expr.hir_id)
|
||||
|
|
|
@ -221,7 +221,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
|||
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
|
||||
match stmt.kind {
|
||||
// we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric`
|
||||
StmtKind::Local(local) => self.ty_bounds.push(ExplicitTyBound(local.ty.is_some())),
|
||||
StmtKind::Let(local) => self.ty_bounds.push(ExplicitTyBound(local.ty.is_some())),
|
||||
|
||||
_ => self.ty_bounds.push(ExplicitTyBound(false)),
|
||||
}
|
||||
|
|
|
@ -423,7 +423,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
|
|||
}
|
||||
},
|
||||
StmtKind::Expr(e) => self.visit_expr(e),
|
||||
StmtKind::Local(l) => {
|
||||
StmtKind::Let(l) => {
|
||||
self.visit_pat(l.pat);
|
||||
if let Some(e) = l.init {
|
||||
self.allow_insert_closure &= !self.in_tail_pos;
|
||||
|
|
|
@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
|||
fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>) -> &'tcx ExprKind<'hir> {
|
||||
if let ExprKind::Block(block, _label @ None) = kind
|
||||
&& let Block {
|
||||
stmts: [Stmt { kind: StmtKind::Local(local), .. }],
|
||||
stmts: [Stmt { kind: StmtKind::Let(local), .. }],
|
||||
expr: Some(expr_end_of_block),
|
||||
rules: BlockCheckMode::DefaultBlock,
|
||||
..
|
||||
|
|
|
@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
|||
let mut it = block.stmts.iter().peekable();
|
||||
while let Some(stmt) = it.next() {
|
||||
if let Some(expr) = it.peek()
|
||||
&& let hir::StmtKind::Local(local) = stmt.kind
|
||||
&& let hir::StmtKind::Let(local) = stmt.kind
|
||||
&& let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
|
||||
&& let hir::StmtKind::Expr(if_) = expr.kind
|
||||
&& let hir::ExprKind::If(
|
||||
|
|
|
@ -410,7 +410,7 @@ fn get_assignments<'a, 'tcx>(
|
|||
stmts
|
||||
.iter()
|
||||
.filter_map(move |stmt| match stmt.kind {
|
||||
StmtKind::Local(..) | StmtKind::Item(..) => None,
|
||||
StmtKind::Let(..) | StmtKind::Item(..) => None,
|
||||
StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e),
|
||||
})
|
||||
.chain(*expr)
|
||||
|
|
|
@ -72,7 +72,7 @@ fn is_vec_pop_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, is_empty_recv: &Expr
|
|||
}
|
||||
|
||||
fn check_local(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, loop_span: Span) {
|
||||
if let StmtKind::Local(local) = stmt.kind
|
||||
if let StmtKind::Let(local) = stmt.kind
|
||||
&& let Some(init) = local.init
|
||||
&& is_vec_pop_unwrap(cx, init, is_empty_recv)
|
||||
{
|
||||
|
|
|
@ -137,7 +137,7 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t
|
|||
match stmt.kind {
|
||||
StmtKind::Semi(e) | StmtKind::Expr(e) => Some((e, None)),
|
||||
// add the let...else expression (if present)
|
||||
StmtKind::Local(local) => local.init.map(|init| (init, local.els)),
|
||||
StmtKind::Let(local) => local.init.map(|init| (init, local.els)),
|
||||
StmtKind::Item(..) => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustc_lint::LateContext;
|
|||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
|
||||
let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) {
|
||||
([stmt, stmts @ ..], expr) => {
|
||||
if let StmtKind::Local(&Local {
|
||||
if let StmtKind::Let(&Local {
|
||||
init: Some(e),
|
||||
els: None,
|
||||
..
|
||||
|
|
|
@ -53,7 +53,7 @@ impl<'tcx> QuestionMark {
|
|||
return;
|
||||
}
|
||||
|
||||
if let StmtKind::Local(local) = stmt.kind
|
||||
if let StmtKind::Let(local) = stmt.kind
|
||||
&& let Some(init) = local.init
|
||||
&& local.els.is_none()
|
||||
&& local.ty.is_none()
|
||||
|
|
|
@ -138,7 +138,7 @@ fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<
|
|||
// If block only contains statements,
|
||||
// reduce `{ X; }` to `X` or `X;`
|
||||
match inner_stmt.kind {
|
||||
hir::StmtKind::Local(local) => Some(local.span),
|
||||
hir::StmtKind::Let(local) => Some(local.span),
|
||||
hir::StmtKind::Expr(e) => Some(e.span),
|
||||
hir::StmtKind::Semi(..) => Some(inner_stmt.span),
|
||||
hir::StmtKind::Item(..) => None,
|
||||
|
|
|
@ -424,7 +424,7 @@ fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v>
|
|||
match stmt.kind {
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)),
|
||||
StmtKind::Item(..) => None,
|
||||
StmtKind::Local(Local { init, pat, .. }) => {
|
||||
StmtKind::Let(Local { init, pat, .. }) => {
|
||||
if let PatKind::Binding(_, hir_id, ..) = pat.kind {
|
||||
init.map(|init_expr| (init_expr, Some(hir_id)))
|
||||
} else {
|
||||
|
|
|
@ -198,7 +198,7 @@ fn indirect_usage<'tcx>(
|
|||
binding: HirId,
|
||||
ctxt: SyntaxContext,
|
||||
) -> Option<IndirectUsage<'tcx>> {
|
||||
if let StmtKind::Local(&Local {
|
||||
if let StmtKind::Let(&Local {
|
||||
pat: Pat {
|
||||
kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None),
|
||||
..
|
||||
|
|
|
@ -27,7 +27,7 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &E
|
|||
|
||||
fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> {
|
||||
for stmt in statements {
|
||||
if let StmtKind::Local(local) = stmt.kind
|
||||
if let StmtKind::Let(local) = stmt.kind
|
||||
&& let Some(init) = local.init
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = init.kind
|
||||
&& let hir::def::Res::Local(local_hir_id) = path.res
|
||||
|
|
|
@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
|
|||
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
if !in_external_macro(cx.tcx.sess, stmt.span)
|
||||
&& let StmtKind::Local(local) = stmt.kind
|
||||
&& let StmtKind::Let(local) = stmt.kind
|
||||
&& let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind
|
||||
&& let Some(init) = local.init
|
||||
// Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue.
|
||||
|
|
|
@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence {
|
|||
}
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
match stmt.kind {
|
||||
StmtKind::Local(local) => {
|
||||
StmtKind::Let(local) => {
|
||||
if let Local { init: Some(e), .. } = local {
|
||||
DivergenceVisitor { cx }.visit_expr(e);
|
||||
}
|
||||
|
@ -291,7 +291,7 @@ fn check_stmt<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, stmt: &'tcx Stmt<'_>) -> St
|
|||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr),
|
||||
// If the declaration is of a local variable, check its initializer
|
||||
// expression if it has one. Otherwise, keep going.
|
||||
StmtKind::Local(local) => local
|
||||
StmtKind::Let(local) => local
|
||||
.init
|
||||
.as_ref()
|
||||
.map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),
|
||||
|
|
|
@ -86,7 +86,7 @@ fn contains_let(cond: &Expr<'_>) -> bool {
|
|||
}
|
||||
|
||||
fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||
let StmtKind::Local(local) = stmt.kind else {
|
||||
let StmtKind::Let(local) = stmt.kind else {
|
||||
return false;
|
||||
};
|
||||
!local.pat.walk_short(|pat| {
|
||||
|
|
|
@ -174,7 +174,7 @@ impl NoEffect {
|
|||
);
|
||||
return true;
|
||||
}
|
||||
} else if let StmtKind::Local(local) = stmt.kind {
|
||||
} else if let StmtKind::Let(local) = stmt.kind {
|
||||
if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id)
|
||||
&& !matches!(local.source, LocalSource::AsyncFn)
|
||||
&& let Some(init) = local.init
|
||||
|
|
|
@ -82,7 +82,7 @@ declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
if let StmtKind::Local(local) = stmt.kind {
|
||||
if let StmtKind::Let(local) = stmt.kind {
|
||||
if in_external_macro(cx.sess(), local.pat.span) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ fn find_let_else_ret_expression<'hir>(block: &'hir Block<'hir>) -> Option<&'hir
|
|||
}
|
||||
|
||||
fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
|
||||
if let StmtKind::Local(Local {
|
||||
if let StmtKind::Let(Local {
|
||||
pat,
|
||||
init: Some(init_expr),
|
||||
els: Some(els),
|
||||
|
|
|
@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
|
|||
return;
|
||||
}
|
||||
|
||||
if let StmtKind::Local(local) = stmt.kind
|
||||
if let StmtKind::Let(local) = stmt.kind
|
||||
&& let Local {
|
||||
pat, init: Some(init), ..
|
||||
} = local
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue