1
Fork 0

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:
bors 2024-03-15 00:03:58 +00:00
commit c2901f5435
143 changed files with 2023 additions and 393 deletions

View file

@ -1021,7 +1021,7 @@ impl Stmt {
#[derive(Clone, Encodable, Decodable, Debug)] #[derive(Clone, Encodable, Decodable, Debug)]
pub enum StmtKind { pub enum StmtKind {
/// A local (let) binding. /// A local (let) binding.
Local(P<Local>), Let(P<Local>),
/// An item definition. /// An item definition.
Item(P<Item>), Item(P<Item>),
/// Expr without trailing semi-colon. /// Expr without trailing semi-colon.

View file

@ -182,7 +182,7 @@ impl<T: HasTokens> HasTokens for Option<T> {
impl HasTokens for StmtKind { impl HasTokens for StmtKind {
fn tokens(&self) -> Option<&LazyAttrTokenStream> { fn tokens(&self) -> Option<&LazyAttrTokenStream> {
match self { match self {
StmtKind::Local(local) => local.tokens.as_ref(), StmtKind::Let(local) => local.tokens.as_ref(),
StmtKind::Item(item) => item.tokens(), StmtKind::Item(item) => item.tokens(),
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.tokens(), StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.tokens(),
StmtKind::Empty => return None, StmtKind::Empty => return None,
@ -191,7 +191,7 @@ impl HasTokens for StmtKind {
} }
fn tokens_mut(&mut self) -> Option<&mut Option<LazyAttrTokenStream>> { fn tokens_mut(&mut self) -> Option<&mut Option<LazyAttrTokenStream>> {
match self { match self {
StmtKind::Local(local) => Some(&mut local.tokens), StmtKind::Let(local) => Some(&mut local.tokens),
StmtKind::Item(item) => item.tokens_mut(), StmtKind::Item(item) => item.tokens_mut(),
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.tokens_mut(), StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.tokens_mut(),
StmtKind::Empty => return None, StmtKind::Empty => return None,
@ -355,7 +355,7 @@ impl HasAttrs for StmtKind {
fn attrs(&self) -> &[Attribute] { fn attrs(&self) -> &[Attribute] {
match self { match self {
StmtKind::Local(local) => &local.attrs, StmtKind::Let(local) => &local.attrs,
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(), StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(),
StmtKind::Item(item) => item.attrs(), StmtKind::Item(item) => item.attrs(),
StmtKind::Empty => &[], StmtKind::Empty => &[],
@ -365,7 +365,7 @@ impl HasAttrs for StmtKind {
fn visit_attrs(&mut self, f: impl FnOnce(&mut AttrVec)) { fn visit_attrs(&mut self, f: impl FnOnce(&mut AttrVec)) {
match self { 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::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f),
StmtKind::Item(item) => item.visit_attrs(f), StmtKind::Item(item) => item.visit_attrs(f),
StmtKind::Empty => {} StmtKind::Empty => {}

View file

@ -1567,7 +1567,7 @@ pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
vis: &mut T, vis: &mut T,
) -> SmallVec<[StmtKind; 1]> { ) -> SmallVec<[StmtKind; 1]> {
match kind { match kind {
StmtKind::Local(mut local) => smallvec![StmtKind::Local({ StmtKind::Let(mut local) => smallvec![StmtKind::Let({
vis.visit_local(&mut local); vis.visit_local(&mut local);
local local
})], })],

View file

@ -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 { pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) -> V::Result {
match &statement.kind { 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::Item(item) => try_visit!(visitor.visit_item(item)),
StmtKind::Expr(expr) | StmtKind::Semi(expr) => try_visit!(visitor.visit_expr(expr)), StmtKind::Expr(expr) | StmtKind::Semi(expr) => try_visit!(visitor.visit_expr(expr)),
StmtKind::Empty => {} StmtKind::Empty => {}

View file

@ -32,11 +32,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let mut expr = None; let mut expr = None;
while let [s, tail @ ..] = ast_stmts { while let [s, tail @ ..] = ast_stmts {
match &s.kind { match &s.kind {
StmtKind::Local(local) => { StmtKind::Let(local) => {
let hir_id = self.lower_node_id(s.id); let hir_id = self.lower_node_id(s.id);
let local = self.lower_local(local); let local = self.lower_local(local);
self.alias_attrs(hir_id, local.hir_id); 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); let span = self.lower_span(s.span);
stmts.push(hir::Stmt { hir_id, kind, span }); stmts.push(hir::Stmt { hir_id, kind, span });
} }

View file

@ -2356,7 +2356,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span: self.lower_span(span), span: self.lower_span(span),
ty: None, 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> { fn block_expr(&mut self, expr: &'hir hir::Expr<'hir>) -> &'hir hir::Block<'hir> {

View file

@ -1212,7 +1212,7 @@ impl<'a> State<'a> {
fn print_stmt(&mut self, st: &ast::Stmt) { fn print_stmt(&mut self, st: &ast::Stmt) {
self.maybe_print_comment(st.span.lo()); self.maybe_print_comment(st.span.lo());
match &st.kind { match &st.kind {
ast::StmtKind::Local(loc) => { ast::StmtKind::Let(loc) => {
self.print_outer_attributes(&loc.attrs); self.print_outer_attributes(&loc.attrs);
self.space_if_not_bol(); self.space_if_not_bol();
self.ibox(INDENT_UNIT); self.ibox(INDENT_UNIT);

View file

@ -616,7 +616,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// FIXME: We make sure that this is a normal top-level binding, // 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 // 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 &ex.kind
&& let hir::PatKind::Binding(..) = pat.kind && let hir::PatKind::Binding(..) = pat.kind
&& span.contains(self.decl_span) && span.contains(self.decl_span)

View file

@ -558,7 +558,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
hir::intravisit::walk_stmt(self, stmt); hir::intravisit::walk_stmt(self, stmt);
let expr = match stmt.kind { let expr = match stmt.kind {
hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, 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; return;
} }
@ -1305,7 +1305,7 @@ struct BindingFinder {
impl<'tcx> Visitor<'tcx> for BindingFinder { impl<'tcx> Visitor<'tcx> for BindingFinder {
type Result = ControlFlow<hir::HirId>; type Result = ControlFlow<hir::HirId>;
fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) -> Self::Result { 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 && local.pat.span == self.span
{ {
ControlFlow::Break(local.hir_id) ControlFlow::Break(local.hir_id)

View file

@ -203,57 +203,63 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
val: &'ll Value, val: &'ll Value,
dst: PlaceRef<'tcx, &'ll Value>, dst: PlaceRef<'tcx, &'ll Value>,
) { ) {
if self.is_ignore() { match &self.mode {
return; PassMode::Ignore => {}
} // Sized indirect arguments
if self.is_sized_indirect() { PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => {
OperandValue::Ref(val, None, self.layout.align.abi).store(bx, dst) let align = attrs.pointee_align.unwrap_or(self.layout.align.abi);
} else if self.is_unsized_indirect() { OperandValue::Ref(val, None, align).store(bx, dst);
bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); }
} else if let PassMode::Cast { cast, pad_i32: _ } = &self.mode { // Unsized indirect qrguments
// FIXME(eddyb): Figure out when the simpler Store is safe, clang PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
// uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. bug!("unsized `ArgAbi` must be handled through `store_fn_arg`");
let can_store_through_cast_ptr = false; }
if can_store_through_cast_ptr { PassMode::Cast { cast, pad_i32: _ } => {
bx.store(val, dst.llval, self.layout.align.abi); // FIXME(eddyb): Figure out when the simpler Store is safe, clang
} else { // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
// The actual return type is a struct, but the ABI let can_store_through_cast_ptr = false;
// adaptation code has cast it into some scalar type. The if can_store_through_cast_ptr {
// code that follows is the only reliable way I have bx.store(val, dst.llval, self.layout.align.abi);
// found to do a transform like i64 -> {i32,i32}. } else {
// Basically we dump the data onto the stack then memcpy it. // The actual return type is a struct, but the ABI
// // adaptation code has cast it into some scalar type. The
// Other approaches I tried: // code that follows is the only reliable way I have
// - Casting rust ret pointer to the foreign type and using Store // found to do a transform like i64 -> {i32,i32}.
// is (a) unsafe if size of foreign type > size of rust type and // Basically we dump the data onto the stack then memcpy it.
// (b) runs afoul of strict aliasing rules, yielding invalid //
// assembly under -O (specifically, the store gets removed). // Other approaches I tried:
// - Truncating foreign type to correct integral type and then // - Casting rust ret pointer to the foreign type and using Store
// bitcasting to the struct type yields invalid cast errors. // is (a) unsafe if size of foreign type > size of rust type and
// (b) runs afoul of strict aliasing rules, yielding invalid
// We instead thus allocate some scratch space... // assembly under -O (specifically, the store gets removed).
let scratch_size = cast.size(bx); // - Truncating foreign type to correct integral type and then
let scratch_align = cast.align(bx); // bitcasting to the struct type yields invalid cast errors.
let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align);
bx.lifetime_start(llscratch, scratch_size); // We instead thus allocate some scratch space...
let scratch_size = cast.size(bx);
// ... where we first store the value... let scratch_align = cast.align(bx);
bx.store(val, llscratch, scratch_align); let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align);
bx.lifetime_start(llscratch, scratch_size);
// ... and then memcpy it to the intended destination.
bx.memcpy( // ... where we first store the value...
dst.llval, bx.store(val, llscratch, scratch_align);
self.layout.align.abi,
llscratch, // ... and then memcpy it to the intended destination.
scratch_align, bx.memcpy(
bx.const_usize(self.layout.size.bytes()), dst.llval,
MemFlags::empty(), self.layout.align.abi,
); llscratch,
scratch_align,
bx.lifetime_end(llscratch, scratch_size); 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);
} }
} }

View file

@ -164,6 +164,15 @@ impl CounterMappingRegion {
end_line, end_line,
end_col, 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( pub(crate) fn branch_region(
counter: Counter, counter: Counter,
false_counter: Counter, false_counter: Counter,

View file

@ -88,7 +88,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
match coverage.kind { match coverage.kind {
// Marker statements have no effect during codegen, // Marker statements have no effect during codegen,
// so return early and don't create `func_coverage`. // 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. // Match exhaustively to ensure that newly-added kinds are classified correctly.
CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } => {} CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } => {}
} }
@ -108,7 +108,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
let Coverage { kind } = coverage; let Coverage { kind } = coverage;
match *kind { match *kind {
CoverageKind::SpanMarker => unreachable!( CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!(
"unexpected marker statement {kind:?} should have caused an early return" "unexpected marker statement {kind:?} should have caused an early return"
), ),
CoverageKind::CounterIncrement { id } => { CoverageKind::CounterIncrement { id } => {

View file

@ -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(()) Ok(())
} }

View file

@ -1640,16 +1640,7 @@ impl<'a> Linker for AixLinker<'a> {
fn ehcont_guard(&mut self) {} fn ehcont_guard(&mut self) {}
fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) { fn debuginfo(&mut self, _: 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 no_crt_objects(&mut self) {} fn no_crt_objects(&mut self) {}

View file

@ -377,29 +377,45 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
} }
} }
if arg.is_sized_indirect() { match arg.mode {
// Don't copy an indirect argument to an alloca, the caller // Sized indirect arguments
// already put it in a temporary alloca and gave it up. PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => {
// FIXME: lifetimes // Don't copy an indirect argument to an alloca, the caller already put it
let llarg = bx.get_param(llarg_idx); // in a temporary alloca and gave it up.
llarg_idx += 1; // FIXME: lifetimes
LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout)) if let Some(pointee_align) = attrs.pointee_align
} else if arg.is_unsized_indirect() { && pointee_align < arg.layout.align.abi
// As the storage for the indirect argument lives during {
// the whole function call, we just copy the fat pointer. // ...unless the argument is underaligned, then we need to copy it to
let llarg = bx.get_param(llarg_idx); // a higher-aligned alloca.
llarg_idx += 1; let tmp = PlaceRef::alloca(bx, arg.layout);
let llextra = bx.get_param(llarg_idx); bx.store_fn_arg(arg, &mut llarg_idx, tmp);
llarg_idx += 1; LocalRef::Place(tmp)
let indirect_operand = OperandValue::Pair(llarg, llextra); } 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); let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout);
indirect_operand.store(bx, tmp); indirect_operand.store(bx, tmp);
LocalRef::UnsizedPlace(tmp) LocalRef::UnsizedPlace(tmp)
} else { }
let tmp = PlaceRef::alloca(bx, arg.layout); _ => {
bx.store_fn_arg(arg, &mut llarg_idx, tmp); let tmp = PlaceRef::alloca(bx, arg.layout);
LocalRef::Place(tmp) bx.store_fn_arg(arg, &mut llarg_idx, tmp);
LocalRef::Place(tmp)
}
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View file

@ -28,7 +28,7 @@ use rustc_errors::{
markdown, ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult, markdown, ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult,
}; };
use rustc_feature::find_gated_cfg; 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_interface::{interface, Queries};
use rustc_lint::unerased_lint_store; use rustc_lint::unerased_lint_store;
use rustc_metadata::creader::MetadataLoader; 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::config::{ErrorOutputType, Input, OutFileName, OutputType};
use rustc_session::getopts::{self, Matches}; use rustc_session::getopts::{self, Matches};
use rustc_session::lint::{Lint, LintId}; use rustc_session::lint::{Lint, LintId};
use rustc_session::output::collect_crate_types;
use rustc_session::{config, filesearch, EarlyDiagCtxt, Session}; use rustc_session::{config, filesearch, EarlyDiagCtxt, Session};
use rustc_span::def_id::LOCAL_CRATE; use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::source_map::FileLoader; use rustc_span::source_map::FileLoader;

View file

@ -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 /// Trait implemented by lint types. This should not be implemented manually. Instead, use
/// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic].

View file

@ -218,7 +218,7 @@ impl<'a> ExtCtxt<'a> {
} }
pub fn stmt_local(&self, local: P<ast::Local>, span: Span) -> ast::Stmt { 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 { pub fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt {

View file

@ -1389,7 +1389,7 @@ impl InvocationCollectorNode for ast::Stmt {
StmtKind::Item(item) => matches!(item.kind, ItemKind::MacCall(..)), StmtKind::Item(item) => matches!(item.kind, ItemKind::MacCall(..)),
StmtKind::Semi(expr) => matches!(expr.kind, ExprKind::MacCall(..)), StmtKind::Semi(expr) => matches!(expr.kind, ExprKind::MacCall(..)),
StmtKind::Expr(..) => unreachable!(), StmtKind::Expr(..) => unreachable!(),
StmtKind::Local(..) | StmtKind::Empty => false, StmtKind::Let(..) | StmtKind::Empty => false,
} }
} }
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) { fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {

View file

@ -1209,7 +1209,7 @@ pub struct Stmt<'hir> {
#[derive(Debug, Clone, Copy, HashStable_Generic)] #[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum StmtKind<'hir> { pub enum StmtKind<'hir> {
/// A local (`let`) binding. /// A local (`let`) binding.
Local(&'hir Local<'hir>), Let(&'hir Local<'hir>),
/// An item binding. /// An item binding.
Item(ItemId), Item(ItemId),

View file

@ -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 { 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)); try_visit!(visitor.visit_id(statement.hir_id));
match statement.kind { 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::Item(item) => visitor.visit_nested_item(item),
StmtKind::Expr(ref expression) | StmtKind::Semi(ref expression) => { StmtKind::Expr(ref expression) | StmtKind::Semi(ref expression) => {
visitor.visit_expr(expression) visitor.visit_expr(expression)

View file

@ -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 /// Check for shared or mutable references of `static mut` inside statement
pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) { 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 && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind
&& matches!(ba.0, rustc_ast::ByRef::Yes) && matches!(ba.0, rustc_ast::ByRef::Yes)
&& let Some(init) = loc.init && let Some(init) = loc.init

View file

@ -123,7 +123,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
for (i, statement) in blk.stmts.iter().enumerate() { for (i, statement) in blk.stmts.iter().enumerate() {
match statement.kind { 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. // Let-else has a special lexical structure for variables.
// First we take a checkpoint of the current scope context here. // First we take a checkpoint of the current scope context here.
let mut prev_cx = visitor.cx; 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. // From now on, we continue normally.
visitor.cx = prev_cx; visitor.cx = prev_cx;
} }
hir::StmtKind::Local(..) => { hir::StmtKind::Let(..) => {
// Each declaration introduces a subscope for bindings // Each declaration introduces a subscope for bindings
// introduced by the declaration; this subscope covers a // introduced by the declaration; this subscope covers a
// suffix of the block. Each subscope in a block has the // suffix of the block. Each subscope in a block has the

View file

@ -864,7 +864,7 @@ impl<'a> State<'a> {
fn print_stmt(&mut self, st: &hir::Stmt<'_>) { fn print_stmt(&mut self, st: &hir::Stmt<'_>) {
self.maybe_print_comment(st.span.lo()); self.maybe_print_comment(st.span.lo());
match st.kind { match st.kind {
hir::StmtKind::Local(loc) => { hir::StmtKind::Let(loc) => {
self.print_local(loc.init, loc.els, |this| this.print_local_decl(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)), 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. /// seen the semicolon, and thus don't need another.
fn stmt_ends_with_semi(stmt: &hir::StmtKind<'_>) -> bool { fn stmt_ends_with_semi(stmt: &hir::StmtKind<'_>) -> bool {
match *stmt { match *stmt {
hir::StmtKind::Local(_) => true, hir::StmtKind::Let(_) => true,
hir::StmtKind::Item(_) => false, hir::StmtKind::Item(_) => false,
hir::StmtKind::Expr(e) => expr_requires_semi_to_be_stmt(e), hir::StmtKind::Expr(e) => expr_requires_semi_to_be_stmt(e),
hir::StmtKind::Semi(..) => false, hir::StmtKind::Semi(..) => false,

View file

@ -371,11 +371,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) { fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) {
match stmt.kind { 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, |_| {}) self.walk_local(expr, pat, *els, |_| {})
} }
hir::StmtKind::Local(_) => {} hir::StmtKind::Let(_) => {}
hir::StmtKind::Item(_) => { hir::StmtKind::Item(_) => {
// We don't visit nested items in this visitor, // We don't visit nested items in this visitor,

View file

@ -1593,7 +1593,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Don't do all the complex logic below for `DeclItem`. // Don't do all the complex logic below for `DeclItem`.
match stmt.kind { match stmt.kind {
hir::StmtKind::Item(..) => return, 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"); 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); let old_diverges = self.diverges.replace(Diverges::Maybe);
match stmt.kind { match stmt.kind {
hir::StmtKind::Local(l) => { hir::StmtKind::Let(l) => {
self.check_decl_local(l); self.check_decl_local(l);
} }
// Ignore for now. // Ignore for now.
@ -1765,7 +1765,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
[ [
hir::Stmt { hir::Stmt {
kind: kind:
hir::StmtKind::Local(hir::Local { hir::StmtKind::Let(hir::Local {
source: source:
hir::LocalSource::AssignDesugar(_), hir::LocalSource::AssignDesugar(_),
.. ..

View file

@ -1599,7 +1599,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn is_local_statement(&self, id: hir::HirId) -> bool { fn is_local_statement(&self, id: hir::HirId) -> bool {
let node = self.tcx.hir_node(id); 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`, /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`,

View file

@ -2221,7 +2221,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
impl<'v> Visitor<'v> for LetVisitor { impl<'v> Visitor<'v> for LetVisitor {
type Result = ControlFlow<Option<&'v hir::Expr<'v>>>; type Result = ControlFlow<Option<&'v hir::Expr<'v>>>;
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { 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 && let Binding(_, _, ident, ..) = pat.kind
&& ident.name == self.ident_name && ident.name == self.ident_name
{ {

View file

@ -217,7 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
bug!(); bug!();
}; };
for stmt in block.stmts { for stmt in block.stmts {
let hir::StmtKind::Local(hir::Local { let hir::StmtKind::Let(hir::Local {
init: Some(init), init: Some(init),
source: hir::LocalSource::AsyncFn, source: hir::LocalSource::AsyncFn,
pat, pat,

View file

@ -110,8 +110,9 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_data_structures::{base_n, flock}; use rustc_data_structures::{base_n, flock};
use rustc_errors::ErrorGuaranteed; use rustc_errors::ErrorGuaranteed;
use rustc_fs_util::{link_or_copy, try_canonicalize, LinkOrCopy}; 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_session::{Session, StableCrateId};
use rustc_span::Symbol;
use std::fs as std_fs; use std::fs as std_fs;
use std::io::{self, ErrorKind}; 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. /// The garbage collection will take care of it.
/// ///
/// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph /// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph
pub(crate) fn prepare_session_directory( pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuaranteed> {
sess: &Session,
crate_name: Symbol,
stable_crate_id: StableCrateId,
) -> Result<(), ErrorGuaranteed> {
if sess.opts.incremental.is_none() { if sess.opts.incremental.is_none() {
return Ok(()); return Ok(());
} }
@ -219,7 +216,7 @@ pub(crate) fn prepare_session_directory(
debug!("prepare_session_directory"); debug!("prepare_session_directory");
// {incr-comp-dir}/{crate-name-and-disambiguator} // {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()); debug!("crate-dir: {}", crate_dir.display());
create_dir(sess, &crate_dir, "crate")?; create_dir(sess, &crate_dir, "crate")?;
@ -604,9 +601,18 @@ fn string_to_timestamp(s: &str) -> Result<SystemTime, &'static str> {
Ok(UNIX_EPOCH + duration) 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 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 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}"); let crate_name = format!("{crate_name}-{stable_crate_id}");

View file

@ -8,8 +8,8 @@ use rustc_middle::query::on_disk_cache::OnDiskCache;
use rustc_serialize::opaque::MemDecoder; use rustc_serialize::opaque::MemDecoder;
use rustc_serialize::Decodable; use rustc_serialize::Decodable;
use rustc_session::config::IncrementalStateAssertion; use rustc_session::config::IncrementalStateAssertion;
use rustc_session::{Session, StableCrateId}; use rustc_session::Session;
use rustc_span::{ErrorGuaranteed, Symbol}; use rustc_span::ErrorGuaranteed;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use super::data::*; 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 /// Setups the dependency graph by loading an existing graph from disk and set up streaming of a
/// new graph to an incremental session directory. /// new graph to an incremental session directory.
pub fn setup_dep_graph( pub fn setup_dep_graph(sess: &Session) -> Result<DepGraph, ErrorGuaranteed> {
sess: &Session,
crate_name: Symbol,
stable_crate_id: StableCrateId,
) -> Result<DepGraph, ErrorGuaranteed> {
// `load_dep_graph` can only be called after `prepare_session_directory`. // `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)); let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess));

View file

@ -2139,7 +2139,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
// the same span as the error and the type is specified. // the same span as the error and the type is specified.
if let hir::Stmt { if let hir::Stmt {
kind: kind:
hir::StmtKind::Local(hir::Local { hir::StmtKind::Let(hir::Local {
init: Some(hir::Expr { span: init_span, .. }), init: Some(hir::Expr { span: init_span, .. }),
ty: Some(array_ty), ty: Some(array_ty),
.. ..

View file

@ -585,7 +585,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
} }
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { 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, span,
pat: hir::Pat { .. }, pat: hir::Pat { .. },
ty: None, ty: None,
@ -824,7 +824,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let hir = self.tcx.hir(); let hir = self.tcx.hir();
for stmt in blk.stmts.iter().rev() { for stmt in blk.stmts.iter().rev() {
let hir::StmtKind::Local(local) = &stmt.kind else { let hir::StmtKind::Let(local) = &stmt.kind else {
continue; continue;
}; };
local.pat.walk(&mut find_compatible_candidates); local.pat.walk(&mut find_compatible_candidates);

View file

@ -48,6 +48,3 @@ interface_rustc_error_unexpected_annotation =
interface_temps_dir_error = interface_temps_dir_error =
failed to find or create the directory specified by `--temps-dir` 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}`

View file

@ -1,7 +1,5 @@
use rustc_macros::Diagnostic; use rustc_macros::Diagnostic;
use rustc_session::config::CrateType;
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
use rustc_target::spec::TargetTriple;
use std::io; use std::io;
use std::path::Path; use std::path::Path;
@ -90,13 +88,6 @@ pub struct FailedWritingFile<'a> {
#[diag(interface_proc_macro_crate_panic_abort)] #[diag(interface_proc_macro_crate_panic_abort)]
pub struct ProcMacroCratePanicAbort; 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)] #[derive(Diagnostic)]
#[diag(interface_multiple_output_types_adaption)] #[diag(interface_multiple_output_types_adaption)]
pub struct MultipleOutputTypesAdaption; pub struct MultipleOutputTypesAdaption;

View file

@ -18,7 +18,7 @@ use rustc_middle::ty::{GlobalCtxt, TyCtxt};
use rustc_serialize::opaque::FileEncodeResult; use rustc_serialize::opaque::FileEncodeResult;
use rustc_session::config::{self, CrateType, OutputFilenames, OutputType}; use rustc_session::config::{self, CrateType, OutputFilenames, OutputType};
use rustc_session::cstore::Untracked; 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_session::Session;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use std::any::Any; 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. // 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_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( let stable_crate_id = StableCrateId::new(
crate_name, crate_name,
crate_types.contains(&CrateType::Executable), crate_types.contains(&CrateType::Executable),
@ -136,7 +136,7 @@ impl<'tcx> Queries<'tcx> {
sess.cfg_version, sess.cfg_version,
); );
let outputs = util::build_output_filenames(&pre_configured_attrs, sess); 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( let cstore = FreezeLock::new(Box::new(CStore::new(
self.compiler.codegen_backend.metadata_loader(), self.compiler.codegen_backend.metadata_loader(),

View file

@ -7,14 +7,15 @@ use rustc_data_structures::sync;
use rustc_metadata::{load_symbol_from_dylib, DylibError}; use rustc_metadata::{load_symbol_from_dylib, DylibError};
use rustc_parse::validate_attr; use rustc_parse::validate_attr;
use rustc_session as session; 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::filesearch::sysroot_candidates;
use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer}; 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::edit_distance::find_best_match_for_name;
use rustc_span::edition::Edition; use rustc_span::edition::Edition;
use rustc_span::symbol::{sym, Symbol}; use rustc_span::symbol::sym;
use rustc_target::spec::Target; use rustc_target::spec::Target;
use session::output::{categorize_crate_type, CRATE_TYPES};
use session::EarlyDiagCtxt; use session::EarlyDiagCtxt;
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
use std::path::{Path, PathBuf}; 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( fn multiple_output_types_to_stdout(
output_types: &OutputTypes, output_types: &OutputTypes,
single_output_file_is_stdout: bool, single_output_file_is_stdout: bool,

View file

@ -989,7 +989,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &
impl EarlyLintPass for UnusedDocComment { impl EarlyLintPass for UnusedDocComment {
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
let kind = match stmt.kind { let kind = match stmt.kind {
ast::StmtKind::Local(..) => "statements", ast::StmtKind::Let(..) => "statements",
// Disabled pending discussion in #78306 // Disabled pending discussion in #78306
ast::StmtKind::Item(..) => return, ast::StmtKind::Item(..) => return,
// expressions will be reported by `check_expr`. // expressions will be reported by `check_expr`.

View file

@ -914,7 +914,7 @@ trait UnusedDelimLint {
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
match s.kind { 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() { if let Some((init, els)) = local.kind.init_else_opt() {
let ctx = match els { let ctx = match els {
None => UnusedDelimsCtx::AssignedValue, None => UnusedDelimsCtx::AssignedValue,
@ -1189,7 +1189,7 @@ impl EarlyLintPass for UnusedParens {
} }
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { 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)); self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false));
} }

View file

@ -557,6 +557,7 @@ declare_lint! {
/// fn main() { /// fn main() {
/// use foo::bar; /// use foo::bar;
/// foo::bar(); /// foo::bar();
/// bar();
/// } /// }
/// ``` /// ```
/// ///

View file

@ -653,7 +653,7 @@ impl<'hir> Map<'hir> {
| Node::ForeignItem(_) | Node::ForeignItem(_)
| Node::TraitItem(_) | Node::TraitItem(_)
| Node::ImplItem(_) | 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(..), .. }) => { Node::Expr(expr @ Expr { kind: ExprKind::If(..) | ExprKind::Match(..), .. }) => {
return Some(expr); return Some(expr);
} }

View file

@ -6,6 +6,7 @@
use crate::mir; use crate::mir;
use crate::query::TyCtxtAt; use crate::query::TyCtxtAt;
use crate::ty::{Ty, TyCtxt}; use crate::ty::{Ty, TyCtxt};
use rustc_span::def_id::LocalDefId;
use rustc_span::DUMMY_SP; use rustc_span::DUMMY_SP;
macro_rules! declare_hooks { macro_rules! declare_hooks {
@ -70,4 +71,10 @@ declare_hooks! {
/// Getting a &core::panic::Location referring to a span. /// Getting a &core::panic::Location referring to a span.
hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue<'tcx>; 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;
} }

View file

@ -2,10 +2,19 @@
use rustc_index::IndexVec; use rustc_index::IndexVec;
use rustc_macros::HashStable; use rustc_macros::HashStable;
use rustc_span::Symbol; use rustc_span::{Span, Symbol};
use std::fmt::{self, Debug, Formatter}; 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! { rustc_index::newtype_index! {
/// ID of a coverage counter. Values ascend from 0. /// ID of a coverage counter. Values ascend from 0.
/// ///
@ -83,6 +92,12 @@ pub enum CoverageKind {
/// codegen. /// codegen.
SpanMarker, 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. /// Marks the point in MIR control flow represented by a coverage counter.
/// ///
/// This is eventually lowered to `llvm.instrprof.increment` in LLVM IR. /// This is eventually lowered to `llvm.instrprof.increment` in LLVM IR.
@ -107,6 +122,7 @@ impl Debug for CoverageKind {
use CoverageKind::*; use CoverageKind::*;
match self { match self {
SpanMarker => write!(fmt, "SpanMarker"), SpanMarker => write!(fmt, "SpanMarker"),
BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()), CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()), ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
} }
@ -163,14 +179,18 @@ pub struct Expression {
pub enum MappingKind { pub enum MappingKind {
/// Associates a normal region of code with a counter/expression/zero. /// Associates a normal region of code with a counter/expression/zero.
Code(CovTerm), Code(CovTerm),
/// Associates a branch region with separate counters for true and false.
Branch { true_term: CovTerm, false_term: CovTerm },
} }
impl MappingKind { impl MappingKind {
/// Iterator over all coverage terms in this mapping kind. /// Iterator over all coverage terms in this mapping kind.
pub fn terms(&self) -> impl Iterator<Item = CovTerm> { 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 { match *self {
Self::Code(term) => one(term), 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 { pub fn map_terms(&self, map_fn: impl Fn(CovTerm) -> CovTerm) -> Self {
match *self { match *self {
Self::Code(term) => Self::Code(map_fn(term)), 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 expressions: IndexVec<ExpressionId, Expression>,
pub mappings: Vec<Mapping>, 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,
}

View file

@ -403,6 +403,12 @@ pub struct Body<'tcx> {
pub tainted_by_errors: Option<ErrorGuaranteed>, 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` /// Per-function coverage information added by the `InstrumentCoverage`
/// pass, to be used in conjunction with the coverage statements injected /// pass, to be used in conjunction with the coverage statements injected
/// into this body's blocks. /// into this body's blocks.
@ -450,6 +456,7 @@ impl<'tcx> Body<'tcx> {
is_polymorphic: false, is_polymorphic: false,
injection_phase: None, injection_phase: None,
tainted_by_errors, tainted_by_errors,
coverage_branch_info: None,
function_coverage_info: None, function_coverage_info: None,
}; };
body.is_polymorphic = body.has_non_region_param(); body.is_polymorphic = body.has_non_region_param();
@ -479,6 +486,7 @@ impl<'tcx> Body<'tcx> {
is_polymorphic: false, is_polymorphic: false,
injection_phase: None, injection_phase: None,
tainted_by_errors: None, tainted_by_errors: None,
coverage_branch_info: None,
function_coverage_info: None, function_coverage_info: None,
}; };
body.is_polymorphic = body.has_non_region_param(); body.is_polymorphic = body.has_non_region_param();

View file

@ -461,6 +461,9 @@ pub fn write_mir_intro<'tcx>(
// Add an empty line before the first block is printed. // Add an empty line before the first block is printed.
writeln!(w)?; 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 { if let Some(function_coverage_info) = &body.function_coverage_info {
write_function_coverage_info(function_coverage_info, w)?; write_function_coverage_info(function_coverage_info, w)?;
} }
@ -468,6 +471,25 @@ pub fn write_mir_intro<'tcx>(
Ok(()) 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( fn write_function_coverage_info(
function_coverage_info: &coverage::FunctionCoverageInfo, function_coverage_info: &coverage::FunctionCoverageInfo,
w: &mut dyn io::Write, w: &mut dyn io::Write,

View file

@ -405,6 +405,7 @@ TrivialTypeTraversalImpls! {
::rustc_hir::HirId, ::rustc_hir::HirId,
::rustc_hir::MatchSource, ::rustc_hir::MatchSource,
::rustc_target::asm::InlineAsmRegOrRegClass, ::rustc_target::asm::InlineAsmRegOrRegClass,
crate::mir::coverage::BlockMarkerId,
crate::mir::coverage::CounterId, crate::mir::coverage::CounterId,
crate::mir::coverage::ExpressionId, crate::mir::coverage::ExpressionId,
crate::mir::Local, crate::mir::Local,

View 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,
});
}
}

View file

@ -60,6 +60,7 @@ pub(super) fn build_custom_mir<'tcx>(
tainted_by_errors: None, tainted_by_errors: None,
injection_phase: None, injection_phase: None,
pass_count: 0, pass_count: 0,
coverage_branch_info: None,
function_coverage_info: None, function_coverage_info: None,
}; };

View file

@ -105,6 +105,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
success_block.unit() success_block.unit()
} }
ExprKind::Unary { op: UnOp::Not, arg } => { 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 local_scope = this.local_scope();
let (success_block, failure_block) = let (success_block, failure_block) =
this.in_if_then_scope(local_scope, expr_span, |this| { 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 else_block = this.cfg.start_new_block();
let term = TerminatorKind::if_(operand, then_block, else_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); let source_info = this.source_info(expr_span);
this.cfg.terminate(block, source_info, term); this.cfg.terminate(block, source_info, term);
this.break_for_else(else_block, source_info); this.break_for_else(else_block, source_info);

View file

@ -234,6 +234,10 @@ struct Builder<'a, 'tcx> {
// the root (most of them do) and saves us from retracing many sub-paths // the root (most of them do) and saves us from retracing many sub-paths
// many times, and rechecking many nodes. // many times, and rechecking many nodes.
lint_level_roots_cache: GrowableBitSet<hir::ItemLocalId>, 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>>; type CaptureMap<'tcx> = SortedIndexMultiMap<usize, hir::HirId, Capture<'tcx>>;
@ -807,6 +811,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
unit_temp: None, unit_temp: None,
var_debug_info: vec![], var_debug_info: vec![],
lint_level_roots_cache: GrowableBitSet::new_empty(), 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); 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()), MirSource::item(self.def_id.to_def_id()),
self.cfg.basic_blocks, self.cfg.basic_blocks,
self.source_scopes, self.source_scopes,
@ -837,7 +842,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.fn_span, self.fn_span,
self.coroutine, self.coroutine,
None, None,
) );
body.coverage_branch_info = self.coverage_branch_info.and_then(|b| b.into_done());
body
} }
fn insert_upvar_arg(&mut self) { fn insert_upvar_arg(&mut self) {
@ -1111,6 +1118,7 @@ pub(crate) fn parse_float_into_scalar(
mod block; mod block;
mod cfg; mod cfg;
mod coverageinfo;
mod custom; mod custom;
mod expr; mod expr;
mod matches; mod matches;

View file

@ -63,7 +63,7 @@ impl<'tcx> Cx<'tcx> {
// ignore for purposes of the MIR // ignore for purposes of the MIR
None None
} }
hir::StmtKind::Local(local) => { hir::StmtKind::Let(local) => {
let remainder_scope = region::Scope { let remainder_scope = region::Scope {
id: block_id, id: block_id,
data: region::ScopeData::Remainder(region::FirstStatementIndex::new( data: region::ScopeData::Remainder(region::FirstStatementIndex::new(

View file

@ -14,7 +14,6 @@ use self::spans::{BcbMapping, BcbMappingKind, CoverageSpans};
use crate::MirPass; use crate::MirPass;
use rustc_middle::hir; use rustc_middle::hir;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::coverage::*; use rustc_middle::mir::coverage::*;
use rustc_middle::mir::{ use rustc_middle::mir::{
self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator, 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(); 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)"); trace!("InstrumentCoverage skipped for {def_id:?} (not eligible)");
return; return;
} }
@ -140,6 +139,10 @@ fn create_mappings<'tcx>(
.filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| { .filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| {
let kind = match bcb_mapping_kind { let kind = match bcb_mapping_kind {
BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)), 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)?; let code_region = make_code_region(source_map, file_name, span, body_span)?;
Some(Mapping { kind, code_region }) 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. /// Function information extracted from HIR by the coverage instrumentor.
#[derive(Debug)] #[derive(Debug)]
struct ExtractedHirInfo { struct ExtractedHirInfo {

View file

@ -1,14 +1,49 @@
use super::*;
use rustc_data_structures::captures::Captures; use rustc_data_structures::captures::Captures;
use rustc_middle::mir::coverage::*; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::{Body, CoverageIdsInfo}; use rustc_middle::mir::coverage::{CounterId, CoverageKind};
use rustc_middle::query::Providers; use rustc_middle::mir::{Body, Coverage, CoverageIdsInfo, Statement, StatementKind};
use rustc_middle::ty::{self}; 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) { 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`. /// Query implementation for `coverage_ids_info`.

View file

@ -13,6 +13,8 @@ mod from_mir;
pub(super) enum BcbMappingKind { pub(super) enum BcbMappingKind {
/// Associates an ordinary executable code span with its corresponding BCB. /// Associates an ordinary executable code span with its corresponding BCB.
Code(BasicCoverageBlock), Code(BasicCoverageBlock),
/// Associates a branch span with BCBs for its true and false arms.
Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock },
} }
#[derive(Debug)] #[derive(Debug)]
@ -66,6 +68,12 @@ pub(super) fn generate_coverage_spans(
// Each span produced by the generator represents an ordinary code region. // Each span produced by the generator represents an ordinary code region.
BcbMapping { kind: BcbMappingKind::Code(bcb), span } 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() { if mappings.is_empty() {
@ -80,6 +88,10 @@ pub(super) fn generate_coverage_spans(
for &BcbMapping { kind, span: _ } in &mappings { for &BcbMapping { kind, span: _ } in &mappings {
match kind { match kind {
BcbMappingKind::Code(bcb) => insert(bcb), BcbMappingKind::Code(bcb) => insert(bcb),
BcbMappingKind::Branch { true_bcb, false_bcb } => {
insert(true_bcb);
insert(false_bcb);
}
} }
} }

View file

@ -1,7 +1,9 @@
use rustc_data_structures::captures::Captures; use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_index::IndexVec;
use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind};
use rustc_middle::mir::{ use rustc_middle::mir::{
self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
TerminatorKind, TerminatorKind,
}; };
use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
@ -9,6 +11,7 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
use crate::coverage::graph::{ use crate::coverage::graph::{
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB, BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
}; };
use crate::coverage::spans::{BcbMapping, BcbMappingKind};
use crate::coverage::ExtractedHirInfo; use crate::coverage::ExtractedHirInfo;
/// Traverses the MIR body to produce an initial collection of coverage-relevant /// 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, /// If the MIR `Statement` has a span contributive to computing coverage spans,
/// return it; otherwise return `None`. /// return it; otherwise return `None`.
fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> { fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
use mir::coverage::CoverageKind;
match statement.kind { match statement.kind {
// These statements have spans that are often outside the scope of the executed source code // These statements have spans that are often outside the scope of the executed source code
// for their parent `BasicBlock`. // for their parent `BasicBlock`.
@ -225,6 +226,11 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
Some(statement.source_info.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 { StatementKind::Coverage(box mir::Coverage {
// These coverage statements should not exist prior to coverage instrumentation. // These coverage statements should not exist prior to coverage instrumentation.
kind: CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } kind: CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. }
@ -358,3 +364,51 @@ impl SpanFromMir {
Self { span, visible_macro, bcb, is_hole } 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<_>>()
}

View file

@ -37,8 +37,9 @@ use rustc_middle::mir::{
LocalDecl, MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, LocalDecl, MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue,
SourceInfo, Statement, StatementKind, TerminatorKind, START_BLOCK, 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::ty::{self, TyCtxt, TypeVisitableExt};
use rustc_middle::util::Providers;
use rustc_span::{source_map::Spanned, sym, DUMMY_SP}; use rustc_span::{source_map::Spanned, sym, DUMMY_SP};
use rustc_trait_selection::traits; use rustc_trait_selection::traits;
@ -124,7 +125,7 @@ pub fn provide(providers: &mut Providers) {
ffi_unwind_calls::provide(providers); ffi_unwind_calls::provide(providers);
shim::provide(providers); shim::provide(providers);
cross_crate_inline::provide(providers); cross_crate_inline::provide(providers);
*providers = Providers { providers.queries = query::Providers {
mir_keys, mir_keys,
mir_const, mir_const,
mir_const_qualif, mir_const_qualif,
@ -139,7 +140,7 @@ pub fn provide(providers: &mut Providers) {
mir_inliner_callees: inline::cycle::mir_inliner_callees, mir_inliner_callees: inline::cycle::mir_inliner_callees,
promoted_mir, promoted_mir,
deduced_param_attrs: deduce_param_attrs::deduced_param_attrs, deduced_param_attrs: deduce_param_attrs::deduced_param_attrs,
..*providers ..providers.queries
}; };
} }

View file

@ -254,7 +254,7 @@ impl<'a> Parser<'a> {
let local = this.parse_local(attrs)?; let local = this.parse_local(attrs)?;
// FIXME - maybe capture semicolon in recovery? // FIXME - maybe capture semicolon in recovery?
Ok(( 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, TrailingToken::None,
)) ))
})?; })?;
@ -278,7 +278,7 @@ impl<'a> Parser<'a> {
} else { } else {
TrailingToken::None 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::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. // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
match &mut local.kind { match &mut local.kind {
LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => { LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
@ -820,7 +820,7 @@ impl<'a> Parser<'a> {
} }
eat_semi = false; eat_semi = false;
} }
StmtKind::Empty | StmtKind::Item(_) | StmtKind::Local(_) | StmtKind::Semi(_) => { StmtKind::Empty | StmtKind::Item(_) | StmtKind::Let(_) | StmtKind::Semi(_) => {
eat_semi = false eat_semi = false
} }
} }

View file

@ -2444,7 +2444,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
// When checking statements ignore expressions, they will be checked later. // 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); self.check_attributes(l.hir_id, stmt.span, Target::Statement, None);
} }
intravisit::walk_stmt(self, stmt) intravisit::walk_stmt(self, stmt)

View file

@ -277,7 +277,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
record_variants!( record_variants!(
(self, s, s.kind, Id::Node(s.hir_id), hir, Stmt, StmtKind), (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) 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) { fn visit_stmt(&mut self, s: &'v ast::Stmt) {
record_variants!( record_variants!(
(self, s, s.kind, Id::None, ast, Stmt, StmtKind), (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) ast_visit::walk_stmt(self, s)
} }

View file

@ -771,7 +771,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
fn propagate_through_stmt(&mut self, stmt: &hir::Stmt<'_>, succ: LiveNode) -> LiveNode { fn propagate_through_stmt(&mut self, stmt: &hir::Stmt<'_>, succ: LiveNode) -> LiveNode {
match stmt.kind { match stmt.kind {
hir::StmtKind::Local(local) => { hir::StmtKind::Let(local) => {
// Note: we mark the variable as defined regardless of whether // Note: we mark the variable as defined regardless of whether
// there is an initializer. Initially I had thought to only mark // there is an initializer. Initially I had thought to only mark
// the live variable as defined if it was initialized, and then we // the live variable as defined if it was initialized, and then we

View file

@ -280,7 +280,7 @@ impl<'tcx> Visitor<'tcx> for CheckInlineAssembly<'tcx> {
fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
match stmt.kind { match stmt.kind {
StmtKind::Item(..) => {} StmtKind::Item(..) => {}
StmtKind::Local(..) => { StmtKind::Let(..) => {
self.items.push((ItemKind::NonAsm, stmt.span)); self.items.push((ItemKind::NonAsm, stmt.span));
} }
StmtKind::Expr(expr) | StmtKind::Semi(expr) => { StmtKind::Expr(expr) | StmtKind::Semi(expr) => {

View file

@ -23,18 +23,19 @@
// - `check_unused` finally emits the diagnostics based on the data generated // - `check_unused` finally emits the diagnostics based on the data generated
// in the last step // in the last step
use crate::imports::ImportKind; use crate::imports::{Import, ImportKind};
use crate::module_to_string; use crate::module_to_string;
use crate::Resolver; use crate::Resolver;
use crate::NameBindingKind; use crate::{LexicalScopeBinding, NameBindingKind};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast::visit::{self, Visitor}; use rustc_ast::visit::{self, Visitor};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
use rustc_data_structures::unord::UnordSet; use rustc_data_structures::unord::UnordSet;
use rustc_errors::{pluralize, MultiSpan}; use rustc_errors::{pluralize, MultiSpan};
use rustc_hir::def::{DefKind, Res}; 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_session::lint::BuiltinLintDiag;
use rustc_span::symbol::{kw, Ident}; use rustc_span::symbol::{kw, Ident};
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
@ -514,8 +515,59 @@ impl Resolver<'_, '_> {
} }
} }
let mut redundant_imports = UnordSet::default();
for import in check_redundant_imports { 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
} }
} }
} }

View file

@ -1306,7 +1306,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
None 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. // This function is only called for single imports.
let ImportKind::Single { let ImportKind::Single {
source, target, ref source_bindings, ref target_bindings, id, .. 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. // Skip if the import is of the form `use source as target` and source != target.
if source != target { if source != target {
return; return false;
} }
// Skip if the import was produced by a macro. // Skip if the import was produced by a macro.
if import.parent_scope.expansion != LocalExpnId::ROOT { if import.parent_scope.expansion != LocalExpnId::ROOT {
return; return false;
} }
// Skip if we are inside a named module (in contrast to an anonymous // 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) if import.used.get() == Some(Used::Other)
|| self.effective_visibilities.is_exported(self.local_def_id(id)) || self.effective_visibilities.is_exported(self.local_def_id(id))
{ {
return; return false;
} }
let mut is_redundant = true; let mut is_redundant = true;
@ -1375,7 +1375,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
format!("the item `{source}` is imported redundantly"), format!("the item `{source}` is imported redundantly"),
BuiltinLintDiag::RedundantImport(redundant_spans, source), BuiltinLintDiag::RedundantImport(redundant_spans, source),
); );
return true;
} }
false
} }
fn resolve_glob_import(&mut self, import: Import<'a>) { fn resolve_glob_import(&mut self, import: Import<'a>) {

View file

@ -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)] #[derive(Default)]
struct DiagMetadata<'ast> { struct DiagMetadata<'ast> {
/// The current trait's associated items' ident, used for diagnostic suggestions. /// 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 ns = if i + 1 == path.len() { ns } else { TypeNS };
let res = self.r.partial_res_map.get(&seg.id?)?.full_res()?; 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)?; let binding = self.resolve_ident_in_lexical_scope(seg.ident, ns, None, None)?;
(res == binding.res()).then_some((seg, binding))
(res == binding.res()).then_some(seg)
}); });
if let Some(unqualified) = unqualified { if let Some((seg, binding)) = unqualified {
self.r.lint_buffer.buffer_lint_with_diagnostic( self.r.potentially_unnecessary_qualifications.push(UnnecessaryQualification {
lint::builtin::UNUSED_QUALIFICATIONS, binding,
finalize.node_id, node_id: finalize.node_id,
finalize.path_span, path_span: finalize.path_span,
"unnecessary qualification", removal_span: path[0].ident.span.until(seg.ident.span),
lint::BuiltinLintDiag::UnusedQualifications { });
removal_span: path[0].ident.span.until(unqualified.ident.span),
},
);
} }
} }
} }

View file

@ -68,7 +68,7 @@ use std::fmt;
use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion};
use imports::{Import, ImportData, ImportKind, NameResolution}; use imports::{Import, ImportData, ImportKind, NameResolution};
use late::{HasGenericParams, PathSource, PatternSource}; use late::{HasGenericParams, PathSource, PatternSource, UnnecessaryQualification};
use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
use crate::effective_visibilities::EffectiveVisibilitiesVisitor; 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 /// 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 /// items are visible in their whole block, while `Res`es only from the place they are defined
/// forward. /// forward.
#[derive(Debug)] #[derive(Debug, Copy, Clone)]
enum LexicalScopeBinding<'a> { enum LexicalScopeBinding<'a> {
Item(NameBinding<'a>), Item(NameBinding<'a>),
Res(Res), Res(Res),
@ -1105,6 +1105,8 @@ pub struct Resolver<'a, 'tcx> {
potentially_unused_imports: Vec<Import<'a>>, potentially_unused_imports: Vec<Import<'a>>,
potentially_unnecessary_qualifications: Vec<UnnecessaryQualification<'a>>,
/// Table for mapping struct IDs into struct constructor IDs, /// Table for mapping struct IDs into struct constructor IDs,
/// it's not used during normal resolution, only for better error reporting. /// it's not used during normal resolution, only for better error reporting.
/// Also includes of list of each fields visibility /// Also includes of list of each fields visibility
@ -1464,6 +1466,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
local_macro_def_scopes: FxHashMap::default(), local_macro_def_scopes: FxHashMap::default(),
name_already_seen: FxHashMap::default(), name_already_seen: FxHashMap::default(),
potentially_unused_imports: Vec::new(), potentially_unused_imports: Vec::new(),
potentially_unnecessary_qualifications: Default::default(),
struct_constructors: Default::default(), struct_constructors: Default::default(),
unused_macros: Default::default(), unused_macros: Default::default(),
unused_macro_rules: Default::default(), unused_macro_rules: Default::default(),

View file

@ -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_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 session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is greater than 5

View file

@ -146,7 +146,7 @@ pub enum InstrumentCoverage {
/// Individual flag values controlled by `-Z coverage-options`. /// Individual flag values controlled by `-Z coverage-options`.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct CoverageOptions { pub struct CoverageOptions {
/// Add branch coverage instrumentation (placeholder flag; not yet implemented). /// Add branch coverage instrumentation.
pub branch: bool, pub branch: bool,
} }

View file

@ -10,7 +10,7 @@ use rustc_macros::Diagnostic;
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple}; use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple};
use crate::parse::ParseSess; use crate::{config::CrateType, parse::ParseSess};
pub struct FeatureGateError { pub struct FeatureGateError {
pub span: MultiSpan, pub span: MultiSpan,
@ -345,6 +345,13 @@ pub(crate) struct BinaryFloatLiteralNotSupported {
pub span: Span, 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( pub fn report_lit_error(
psess: &ParseSess, psess: &ParseSess,
err: LitError, err: LitError,

View file

@ -1,7 +1,7 @@
//! Related to out filenames of compilation (e.g. binaries). //! 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::{ use crate::errors::{
CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable, self, CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable,
InvalidCharacterInCrateName, InvalidCrateNameHelp, InvalidCharacterInCrateName, InvalidCrateNameHelp,
}; };
use crate::Session; use crate::Session;
@ -200,3 +200,64 @@ pub fn invalid_output_for_target(sess: &Session, crate_type: CrateType) -> bool
false 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
}

View file

@ -633,10 +633,8 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
/// If the resulting alignment differs from the type's alignment, /// If the resulting alignment differs from the type's alignment,
/// the argument will be copied to an alloca with sufficient 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) /// 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. /// 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>) { pub fn make_indirect_byval(&mut self, byval_align: Option<Align>) {
assert!(!self.layout.is_unsized(), "used byval ABI for unsized layout"); assert!(!self.layout.is_unsized(), "used byval ABI for unsized layout");
self.make_indirect(); self.make_indirect();

View file

@ -763,7 +763,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?); let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?);
match self.tcx.parent_hir_node(hir_id) { 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) get_name(err, &local.pat.kind)
} }
// Different to previous arm because one is `&hir::Local` and the other // Different to previous arm because one is `&hir::Local` and the other

View file

@ -856,6 +856,48 @@ impl Duration {
(self.secs as f32) + (self.nanos.0 as f32) / (NANOS_PER_SEC as f32) (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 /// Creates a new `Duration` from the specified number of seconds represented
/// as `f64`. /// as `f64`.
/// ///

View file

@ -13,4 +13,4 @@ core = { path = "../core" }
compiler_builtins = { version = "0.1.0", features = ['rustc-dep-of-std'] } compiler_builtins = { version = "0.1.0", features = ['rustc-dep-of-std'] }
[build-dependencies] [build-dependencies]
cc = "1.0.69" cc = "1.0.90"

View file

@ -39,6 +39,13 @@ pub trait CommandExt: Sealed {
/// Sets the child process's user ID. This translates to a /// Sets the child process's user ID. This translates to a
/// `setuid` call in the child process. Failure in the `setuid` /// `setuid` call in the child process. Failure in the `setuid`
/// call will cause the spawn to fail. /// 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")] #[stable(feature = "rust1", since = "1.0.0")]
fn uid(&mut self, id: UserId) -> &mut process::Command; fn uid(&mut self, id: UserId) -> &mut process::Command;

View file

@ -330,14 +330,22 @@ impl Command {
if let Some(u) = self.get_uid() { if let Some(u) = self.get_uid() {
// When dropping privileges from root, the `setgroups` call // When dropping privileges from root, the `setgroups` call
// will remove any extraneous groups. We only drop groups // 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 // 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 // uid has dropped, we may still have groups that enable us to
// do super-user things. // do super-user things.
//FIXME: Redox kernel does not support setgroups yet //FIXME: Redox kernel does not support setgroups yet
#[cfg(not(target_os = "redox"))] #[cfg(not(target_os = "redox"))]
if libc::getuid() == 0 && self.get_groups().is_none() { if self.get_groups().is_none() {
cvt(libc::setgroups(0, crate::ptr::null()))?; 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))?; cvt(libc::setuid(u as uid_t))?;
} }

View file

@ -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. instrumentation. Pass one or more of the following values, separated by commas.
- `branch` or `no-branch` - `branch` or `no-branch`
- Placeholder for potential branch coverage support in the future. - Enables or disables branch coverage instrumentation.
## Other references ## Other references

View file

@ -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: 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.

View file

@ -52,7 +52,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
.as_ref() .as_ref()
.map_or(false, |e| is_relevant_expr(cx, typeck_results, e)), .map_or(false, |e| is_relevant_expr(cx, typeck_results, e)),
|stmt| match &stmt.kind { |stmt| match &stmt.kind {
StmtKind::Local(_) => true, StmtKind::Let(_) => true,
StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr), StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
StmtKind::Item(_) => false, StmtKind::Item(_) => false,
}, },

View file

@ -349,7 +349,7 @@ impl BlockEq {
/// If the statement is a local, checks if the bound names match the expected list of names. /// 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 { 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 i = 0usize;
let mut res = true; let mut res = true;
l.pat.each_binding_or_first(&mut |_, _, _, name| { l.pat.each_binding_or_first(&mut |_, _, _, name| {
@ -389,7 +389,7 @@ fn eq_stmts(
eq: &mut HirEqInterExpr<'_, '_, '_>, eq: &mut HirEqInterExpr<'_, '_, '_>,
moved_bindings: &mut Vec<(HirId, Symbol)>, moved_bindings: &mut Vec<(HirId, Symbol)>,
) -> bool { ) -> bool {
(if let StmtKind::Local(l) = stmt.kind { (if let StmtKind::Let(l) = stmt.kind {
let old_count = moved_bindings.len(); let old_count = moved_bindings.len();
l.pat.each_binding_or_first(&mut |_, id, _, name| { l.pat.each_binding_or_first(&mut |_, id, _, name| {
moved_bindings.push((id, name.name)); moved_bindings.push((id, name.name));
@ -432,7 +432,7 @@ fn scan_block_for_eq<'tcx>(
.iter() .iter()
.enumerate() .enumerate()
.find(|&(i, stmt)| { .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)) && needs_ordered_drop(cx, cx.typeck_results().node_type(l.hir_id))
{ {
local_needs_ordered_drop = true; 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. // Clear out all locals seen at the end so far. None of them can be moved.
let stmts = &blocks[0].stmts; let stmts = &blocks[0].stmts;
for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] { 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, _, _| { l.pat.each_binding_or_first(&mut |_, id, _, _| {
// FIXME(rust/#120456) - is `swap_remove` correct? // FIXME(rust/#120456) - is `swap_remove` correct?
eq.locals.swap_remove(&id); eq.locals.swap_remove(&id);

View file

@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the // 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 // `default` method of the `Default` trait, and store statement index in current block being
// checked and the name of the bound variable // 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 // only take `let ...` statements
&& let Some(expr) = local.init && let Some(expr) = local.init
&& !any_parent_is_automatically_derived(cx.tcx, expr.hir_id) && !any_parent_is_automatically_derived(cx.tcx, expr.hir_id)

View file

@ -221,7 +221,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
match stmt.kind { match stmt.kind {
// we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric` // 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)), _ => self.ty_bounds.push(ExplicitTyBound(false)),
} }

View file

@ -423,7 +423,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
} }
}, },
StmtKind::Expr(e) => self.visit_expr(e), StmtKind::Expr(e) => self.visit_expr(e),
StmtKind::Local(l) => { StmtKind::Let(l) => {
self.visit_pat(l.pat); self.visit_pat(l.pat);
if let Some(e) = l.init { if let Some(e) = l.init {
self.allow_insert_closure &= !self.in_tail_pos; self.allow_insert_closure &= !self.in_tail_pos;

View file

@ -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> { fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>) -> &'tcx ExprKind<'hir> {
if let ExprKind::Block(block, _label @ None) = kind if let ExprKind::Block(block, _label @ None) = kind
&& let Block { && let Block {
stmts: [Stmt { kind: StmtKind::Local(local), .. }], stmts: [Stmt { kind: StmtKind::Let(local), .. }],
expr: Some(expr_end_of_block), expr: Some(expr_end_of_block),
rules: BlockCheckMode::DefaultBlock, rules: BlockCheckMode::DefaultBlock,
.. ..

View file

@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
let mut it = block.stmts.iter().peekable(); let mut it = block.stmts.iter().peekable();
while let Some(stmt) = it.next() { while let Some(stmt) = it.next() {
if let Some(expr) = it.peek() 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::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
&& let hir::StmtKind::Expr(if_) = expr.kind && let hir::StmtKind::Expr(if_) = expr.kind
&& let hir::ExprKind::If( && let hir::ExprKind::If(

View file

@ -410,7 +410,7 @@ fn get_assignments<'a, 'tcx>(
stmts stmts
.iter() .iter()
.filter_map(move |stmt| match stmt.kind { .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), StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e),
}) })
.chain(*expr) .chain(*expr)

View file

@ -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) { 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 && let Some(init) = local.init
&& is_vec_pop_unwrap(cx, init, is_empty_recv) && is_vec_pop_unwrap(cx, init, is_empty_recv)
{ {

View file

@ -137,7 +137,7 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t
match stmt.kind { match stmt.kind {
StmtKind::Semi(e) | StmtKind::Expr(e) => Some((e, None)), StmtKind::Semi(e) | StmtKind::Expr(e) => Some((e, None)),
// add the let...else expression (if present) // 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, StmtKind::Item(..) => None,
} }
} }

View file

@ -11,7 +11,7 @@ use rustc_lint::LateContext;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { 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) { let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) {
([stmt, stmts @ ..], expr) => { ([stmt, stmts @ ..], expr) => {
if let StmtKind::Local(&Local { if let StmtKind::Let(&Local {
init: Some(e), init: Some(e),
els: None, els: None,
.. ..

View file

@ -53,7 +53,7 @@ impl<'tcx> QuestionMark {
return; return;
} }
if let StmtKind::Local(local) = stmt.kind if let StmtKind::Let(local) = stmt.kind
&& let Some(init) = local.init && let Some(init) = local.init
&& local.els.is_none() && local.els.is_none()
&& local.ty.is_none() && local.ty.is_none()

View file

@ -138,7 +138,7 @@ fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<
// If block only contains statements, // If block only contains statements,
// reduce `{ X; }` to `X` or `X;` // reduce `{ X; }` to `X` or `X;`
match inner_stmt.kind { 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::Expr(e) => Some(e.span),
hir::StmtKind::Semi(..) => Some(inner_stmt.span), hir::StmtKind::Semi(..) => Some(inner_stmt.span),
hir::StmtKind::Item(..) => None, hir::StmtKind::Item(..) => None,

View file

@ -424,7 +424,7 @@ fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v>
match stmt.kind { match stmt.kind {
StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)), StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)),
StmtKind::Item(..) => None, StmtKind::Item(..) => None,
StmtKind::Local(Local { init, pat, .. }) => { StmtKind::Let(Local { init, pat, .. }) => {
if let PatKind::Binding(_, hir_id, ..) = pat.kind { if let PatKind::Binding(_, hir_id, ..) = pat.kind {
init.map(|init_expr| (init_expr, Some(hir_id))) init.map(|init_expr| (init_expr, Some(hir_id)))
} else { } else {

View file

@ -198,7 +198,7 @@ fn indirect_usage<'tcx>(
binding: HirId, binding: HirId,
ctxt: SyntaxContext, ctxt: SyntaxContext,
) -> Option<IndirectUsage<'tcx>> { ) -> Option<IndirectUsage<'tcx>> {
if let StmtKind::Local(&Local { if let StmtKind::Let(&Local {
pat: Pat { pat: Pat {
kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None), kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None),
.. ..

View file

@ -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> { fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> {
for stmt in statements { for stmt in statements {
if let StmtKind::Local(local) = stmt.kind if let StmtKind::Let(local) = stmt.kind
&& let Some(init) = local.init && let Some(init) = local.init
&& let ExprKind::Path(QPath::Resolved(_, path)) = init.kind && let ExprKind::Path(QPath::Resolved(_, path)) = init.kind
&& let hir::def::Res::Local(local_hir_id) = path.res && let hir::def::Res::Local(local_hir_id) = path.res

View file

@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if !in_external_macro(cx.tcx.sess, stmt.span) 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 PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind
&& let Some(init) = local.init && let Some(init) = local.init
// Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue.

View file

@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence {
} }
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
match stmt.kind { match stmt.kind {
StmtKind::Local(local) => { StmtKind::Let(local) => {
if let Local { init: Some(e), .. } = local { if let Local { init: Some(e), .. } = local {
DivergenceVisitor { cx }.visit_expr(e); 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), StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr),
// If the declaration is of a local variable, check its initializer // If the declaration is of a local variable, check its initializer
// expression if it has one. Otherwise, keep going. // expression if it has one. Otherwise, keep going.
StmtKind::Local(local) => local StmtKind::Let(local) => local
.init .init
.as_ref() .as_ref()
.map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)), .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),

View file

@ -86,7 +86,7 @@ fn contains_let(cond: &Expr<'_>) -> bool {
} }
fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> 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; return false;
}; };
!local.pat.walk_short(|pat| { !local.pat.walk_short(|pat| {

View file

@ -174,7 +174,7 @@ impl NoEffect {
); );
return true; 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) if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id)
&& !matches!(local.source, LocalSource::AsyncFn) && !matches!(local.source, LocalSource::AsyncFn)
&& let Some(init) = local.init && let Some(init) = local.init

View file

@ -82,7 +82,7 @@ declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { 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) { if in_external_macro(cx.sess(), local.pat.span) {
return; return;
} }

View file

@ -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<'_>) { fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
if let StmtKind::Local(Local { if let StmtKind::Let(Local {
pat, pat,
init: Some(init_expr), init: Some(init_expr),
els: Some(els), els: Some(els),

View file

@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
return; return;
} }
if let StmtKind::Local(local) = stmt.kind if let StmtKind::Let(local) = stmt.kind
&& let Local { && let Local {
pat, init: Some(init), .. pat, init: Some(init), ..
} = local } = local

Some files were not shown because too many files have changed in this diff Show more