Auto merge of #83855 - Dylan-DPC:rollup-oww62sh, r=Dylan-DPC

Rollup of 8 pull requests

Successful merges:

 - #73945 (Add an unstable --json=unused-externs flag to print unused externs)
 - #81619 (Implement `SourceIterator` and `InPlaceIterable` for `ResultShunt`)
 - #82726 (BTree: move blocks around in node.rs)
 - #83521 (2229: Fix diagnostic issue when using FakeReads in closures)
 - #83532 (Fix compiletest on FreeBSD)
 - #83793 (rustdoc: highlight macros more efficiently)
 - #83809 (Remove unneeded INITIAL_IDS const)
 - #83827 (cleanup leak after test to make miri happy)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2021-04-04 17:48:41 +00:00
commit 8ad6a443cf
59 changed files with 576 additions and 311 deletions

View File

@ -195,6 +195,9 @@ pub trait Emitter {
fn emit_future_breakage_report(&mut self, _diags: Vec<(FutureBreakage, Diagnostic)>) {}
/// Emit list of unused externs
fn emit_unused_externs(&mut self, _lint_level: &str, _unused_externs: &[&str]) {}
/// Checks if should show explanations about "rustc --explain"
fn should_show_explain(&self) -> bool {
true

View File

@ -159,6 +159,19 @@ impl Emitter for JsonEmitter {
}
}
fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) {
let data = UnusedExterns { lint_level, unused_extern_names: unused_externs };
let result = if self.pretty {
writeln!(&mut self.dst, "{}", as_pretty_json(&data))
} else {
writeln!(&mut self.dst, "{}", as_json(&data))
}
.and_then(|_| self.dst.flush());
if let Err(e) = result {
panic!("failed to print unused externs: {:?}", e);
}
}
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
Some(&self.sm)
}
@ -322,6 +335,18 @@ struct FutureIncompatReport {
future_incompat_report: Vec<FutureBreakageItem>,
}
// NOTE: Keep this in sync with the equivalent structs in rustdoc's
// doctest component (as well as cargo).
// We could unify this struct the one in rustdoc but they have different
// ownership semantics, so doing so would create wasteful allocations.
#[derive(Encodable)]
struct UnusedExterns<'a, 'b, 'c> {
/// The severity level of the unused dependencies lint
lint_level: &'a str,
/// List of unused externs by their names.
unused_extern_names: &'b [&'c str],
}
impl Diagnostic {
fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
let sugg = diag.suggestions.iter().map(|sugg| Diagnostic {

View File

@ -765,6 +765,10 @@ impl Handler {
self.inner.borrow_mut().emitter.emit_future_breakage_report(diags)
}
pub fn emit_unused_externs(&self, lint_level: &str, unused_externs: &[&str]) {
self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs)
}
pub fn delay_as_bug(&self, diagnostic: Diagnostic) {
self.inner.borrow_mut().delay_as_bug(diagnostic)
}
@ -839,6 +843,10 @@ impl HandlerInner {
self.emitter.emit_artifact_notification(path, artifact_type);
}
fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) {
self.emitter.emit_unused_externs(lint_level, unused_externs);
}
fn treat_err_as_bug(&self) -> bool {
self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() >= c.get())
}

View File

@ -16,6 +16,7 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_hir::definitions::Definitions;
use rustc_hir::Crate;
use rustc_lint::LintStore;
use rustc_metadata::creader::CStore;
use rustc_middle::arena::Arena;
use rustc_middle::dep_graph::DepGraph;
use rustc_middle::middle;
@ -831,6 +832,12 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> {
});
sess.time("looking_for_derive_registrar", || proc_macro_decls::find(tcx));
let cstore = tcx
.cstore_as_any()
.downcast_ref::<CStore>()
.expect("`tcx.cstore` is not a `CStore`");
cstore.report_unused_deps(tcx);
},
{
par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| {

View File

@ -46,6 +46,9 @@ pub struct CStore {
/// This map is used to verify we get no hash conflicts between
/// `StableCrateId` values.
stable_crate_ids: FxHashMap<StableCrateId, CrateNum>,
/// Unused externs of the crate
unused_externs: Vec<Symbol>,
}
pub struct CrateLoader<'a> {
@ -190,6 +193,27 @@ impl CStore {
crate fn has_global_allocator(&self) -> bool {
self.has_global_allocator
}
pub fn report_unused_deps(&self, tcx: TyCtxt<'_>) {
// We put the check for the option before the lint_level_at_node call
// because the call mutates internal state and introducing it
// leads to some ui tests failing.
if !tcx.sess.opts.json_unused_externs {
return;
}
let level = tcx
.lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID)
.0;
if level != lint::Level::Allow {
let unused_externs =
self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();
let unused_externs = unused_externs.iter().map(String::as_str).collect::<Vec<&str>>();
tcx.sess
.parse_sess
.span_diagnostic
.emit_unused_externs(level.as_str(), &unused_externs);
}
}
}
impl<'a> CrateLoader<'a> {
@ -217,6 +241,7 @@ impl<'a> CrateLoader<'a> {
allocator_kind: None,
has_global_allocator: false,
stable_crate_ids,
unused_externs: Vec::new(),
},
used_extern_options: Default::default(),
}
@ -904,11 +929,17 @@ impl<'a> CrateLoader<'a> {
// Don't worry about pathless `--extern foo` sysroot references
continue;
}
if self.used_extern_options.contains(&Symbol::intern(name)) {
let name_interned = Symbol::intern(name);
if self.used_extern_options.contains(&name_interned) {
continue;
}
// Got a real unused --extern
if self.sess.opts.json_unused_externs {
self.cstore.unused_externs.push(name_interned);
continue;
}
let diag = match self.sess.opts.extern_dep_specs.get(name) {
Some(loc) => BuiltinLintDiagnostics::ExternDepSpec(name.clone(), loc.into()),
None => {
@ -941,9 +972,9 @@ impl<'a> CrateLoader<'a> {
self.inject_allocator_crate(krate);
self.inject_panic_runtime(krate);
info!("{:?}", CrateDump(&self.cstore));
self.report_unused_deps(krate);
info!("{:?}", CrateDump(&self.cstore));
}
pub fn process_extern_crate(

View File

@ -1482,7 +1482,7 @@ pub enum StatementKind<'tcx> {
///
/// Note that this also is emitted for regular `let` bindings to ensure that locals that are
/// never accessed still get some sanity checks for, e.g., `let x: ! = ..;`
FakeRead(FakeReadCause, Box<Place<'tcx>>),
FakeRead(Box<(FakeReadCause, Place<'tcx>)>),
/// Write the discriminant for a variant to the enum Place.
SetDiscriminant { place: Box<Place<'tcx>>, variant_index: VariantIdx },
@ -1575,7 +1575,12 @@ pub enum FakeReadCause {
/// `let x: !; match x {}` doesn't generate any read of x so we need to
/// generate a read of x to check that it is initialized and safe.
ForMatchedPlace,
///
/// If a closure pattern matches a Place starting with an Upvar, then we introduce a
/// FakeRead for that Place outside the closure, in such a case this option would be
/// Some(closure_def_id).
/// Otherwise, the value of the optional DefId will be None.
ForMatchedPlace(Option<DefId>),
/// A fake read of the RefWithinGuard version of a bind-by-value variable
/// in a match guard to ensure that it's value hasn't change by the time
@ -1594,7 +1599,12 @@ pub enum FakeReadCause {
/// but in some cases it can affect the borrow checker, as in #53695.
/// Therefore, we insert a "fake read" here to ensure that we get
/// appropriate errors.
ForLet,
///
/// If a closure pattern matches a Place starting with an Upvar, then we introduce a
/// FakeRead for that Place outside the closure, in such a case this option would be
/// Some(closure_def_id).
/// Otherwise, the value of the optional DefId will be None.
ForLet(Option<DefId>),
/// If we have an index expression like
///
@ -1618,7 +1628,9 @@ impl Debug for Statement<'_> {
use self::StatementKind::*;
match self.kind {
Assign(box (ref place, ref rv)) => write!(fmt, "{:?} = {:?}", place, rv),
FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place),
FakeRead(box (ref cause, ref place)) => {
write!(fmt, "FakeRead({:?}, {:?})", cause, place)
}
Retag(ref kind, ref place) => write!(
fmt,
"Retag({}{:?})",

View File

@ -380,7 +380,7 @@ macro_rules! make_mir_visitor {
) => {
self.visit_assign(place, rvalue, location);
}
StatementKind::FakeRead(_, place) => {
StatementKind::FakeRead(box (_, place)) => {
self.visit_place(
place,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect),

View File

@ -1728,7 +1728,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
match statement {
Statement { kind: StatementKind::FakeRead(cause, box place), .. }
Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
if *place == self.place =>
{
self.cause = Some(*cause);

View File

@ -515,7 +515,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let block = &self.body.basic_blocks()[location.block];
let kind = if let Some(&Statement {
kind: StatementKind::FakeRead(FakeReadCause::ForLet, _),
kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)),
..
}) = block.statements.get(location.statement_index)
{

View File

@ -7,8 +7,8 @@ use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItemGroup;
use rustc_hir::GeneratorKind;
use rustc_middle::mir::{
AggregateKind, Constant, Field, Local, LocalInfo, LocalKind, Location, Operand, Place,
PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand,
Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
};
use rustc_middle::ty::print::Print;
use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt};
@ -795,6 +795,24 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
// StatementKind::FakeRead only contains a def_id if they are introduced as a result
// of pattern matching within a closure.
if let StatementKind::FakeRead(box (cause, ref place)) = stmt.kind {
match cause {
FakeReadCause::ForMatchedPlace(Some(closure_def_id))
| FakeReadCause::ForLet(Some(closure_def_id)) => {
debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
let places = &[Operand::Move(*place)];
if let Some((args_span, generator_kind, var_span)) =
self.closure_span(closure_def_id, moved_place, places)
{
return ClosureUse { generator_kind, args_span, var_span };
}
}
_ => {}
}
}
let normal_ret =
if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
PatUse(stmt.source_info.span)

View File

@ -63,7 +63,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
self.mutate_place(location, *lhs, Shallow(None), JustWrite);
}
StatementKind::FakeRead(_, _) => {
StatementKind::FakeRead(box (_, _)) => {
// Only relevant for initialized/liveness/safety checks.
}
StatementKind::SetDiscriminant { place, variant_index: _ } => {

View File

@ -574,7 +574,7 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc
self.mutate_place(location, (*lhs, span), Shallow(None), JustWrite, flow_state);
}
StatementKind::FakeRead(_, box ref place) => {
StatementKind::FakeRead(box (_, ref place)) => {
// Read for match doesn't access any memory and is used to
// assert that a place is safe and live. So we don't have to
// do any checks here.

View File

@ -293,8 +293,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
}
self.gather_rvalue(rval);
}
StatementKind::FakeRead(_, place) => {
self.create_move_path(**place);
StatementKind::FakeRead(box (_, place)) => {
self.create_move_path(*place);
}
StatementKind::LlvmInlineAsm(ref asm) => {
for (output, kind) in iter::zip(&*asm.outputs, &asm.asm.outputs) {

View File

@ -683,10 +683,10 @@ pub(super) fn filtered_statement_span(
// and `_1` is the `Place` for `somenum`.
//
// If and when the Issue is resolved, remove this special case match pattern:
StatementKind::FakeRead(cause, _) if cause == FakeReadCause::ForGuardBinding => None,
StatementKind::FakeRead(box (cause, _)) if cause == FakeReadCause::ForGuardBinding => None,
// Retain spans from all other statements
StatementKind::FakeRead(_, _) // Not including `ForGuardBinding`
StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding`
| StatementKind::CopyNonOverlapping(..)
| StatementKind::Assign(_)
| StatementKind::SetDiscriminant { .. }

View File

@ -80,7 +80,7 @@ impl<'tcx> CFG<'tcx> {
cause: FakeReadCause,
place: Place<'tcx>,
) {
let kind = StatementKind::FakeRead(cause, box place);
let kind = StatementKind::FakeRead(box (cause, place));
let stmt = Statement { source_info, kind };
self.push(block, stmt);
}

View File

@ -179,24 +179,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// match x { _ => () } // fake read of `x`
// };
// ```
// FIXME(RFC2229): Remove feature gate once diagnostics are improved
if this.tcx.features().capture_disjoint_fields {
for (thir_place, cause, hir_id) in fake_reads.into_iter() {
let place_builder =
unpack!(block = this.as_place_builder(block, thir_place));
for (thir_place, cause, hir_id) in fake_reads.into_iter() {
let place_builder = unpack!(block = this.as_place_builder(block, thir_place));
if let Ok(place_builder_resolved) =
place_builder.try_upvars_resolved(this.tcx, this.typeck_results)
{
let mir_place =
place_builder_resolved.into_place(this.tcx, this.typeck_results);
this.cfg.push_fake_read(
block,
this.source_info(this.tcx.hir().span(*hir_id)),
*cause,
mir_place,
);
}
if let Ok(place_builder_resolved) =
place_builder.try_upvars_resolved(this.tcx, this.typeck_results)
{
let mir_place =
place_builder_resolved.into_place(this.tcx, this.typeck_results);
this.cfg.push_fake_read(
block,
this.source_info(this.tcx.hir().span(*hir_id)),
*cause,
mir_place,
);
}
}

View File

@ -139,7 +139,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// uninhabited value. If we get never patterns, those will check that
// the place is initialized, and so this read would only be used to
// check safety.
let cause_matched_place = FakeReadCause::ForMatchedPlace;
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
let source_info = self.source_info(scrutinee_span);
if let Ok(scrutinee_builder) =
@ -400,7 +400,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let source_info = self.source_info(irrefutable_pat.span);
self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet, place);
self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet(None), place);
self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
block.unit()
@ -435,7 +435,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let pattern_source_info = self.source_info(irrefutable_pat.span);
let cause_let = FakeReadCause::ForLet;
let cause_let = FakeReadCause::ForLet(None);
self.cfg.push_fake_read(block, pattern_source_info, cause_let, place);
let ty_source_info = self.source_info(user_ty_span);

View File

@ -456,6 +456,10 @@ impl Externs {
pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> {
self.0.iter()
}
pub fn len(&self) -> usize {
self.0.len()
}
}
impl ExternEntry {
@ -698,6 +702,7 @@ impl Default for Options {
remap_path_prefix: Vec::new(),
edition: DEFAULT_EDITION,
json_artifact_notifications: false,
json_unused_externs: false,
pretty: None,
}
}
@ -1196,15 +1201,23 @@ pub fn parse_color(matches: &getopts::Matches) -> ColorConfig {
}
}
/// Possible json config files
pub struct JsonConfig {
pub json_rendered: HumanReadableErrorType,
pub json_artifact_notifications: bool,
pub json_unused_externs: bool,
}
/// Parse the `--json` flag.
///
/// The first value returned is how to render JSON diagnostics, and the second
/// is whether or not artifact notifications are enabled.
pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool) {
pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType =
HumanReadableErrorType::Default;
let mut json_color = ColorConfig::Never;
let mut json_artifact_notifications = false;
let mut json_unused_externs = false;
for option in matches.opt_strs("json") {
// For now conservatively forbid `--color` with `--json` since `--json`
// won't actually be emitting any colors and anything colorized is
@ -1221,6 +1234,7 @@ pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool)
"diagnostic-short" => json_rendered = HumanReadableErrorType::Short,
"diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
"artifacts" => json_artifact_notifications = true,
"unused-externs" => json_unused_externs = true,
s => early_error(
ErrorOutputType::default(),
&format!("unknown `--json` option `{}`", s),
@ -1228,7 +1242,12 @@ pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool)
}
}
}
(json_rendered(json_color), json_artifact_notifications)
JsonConfig {
json_rendered: json_rendered(json_color),
json_artifact_notifications,
json_unused_externs,
}
}
/// Parses the `--error-format` flag.
@ -1806,7 +1825,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let edition = parse_crate_edition(matches);
let (json_rendered, json_artifact_notifications) = parse_json(matches);
let JsonConfig { json_rendered, json_artifact_notifications, json_unused_externs } =
parse_json(matches);
let error_format = parse_error_format(matches, color, json_rendered);
@ -1819,6 +1839,14 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let mut debugging_opts = build_debugging_options(matches, error_format);
check_debug_option_stability(&debugging_opts, error_format, json_rendered);
if !debugging_opts.unstable_options && json_unused_externs {
early_error(
error_format,
"the `-Z unstable-options` flag must also be passed to enable \
the flag `--json=unused-externs`",
);
}
let output_types = parse_output_types(&debugging_opts, matches, error_format);
let mut cg = build_codegen_options(matches, error_format);
@ -1979,6 +2007,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
remap_path_prefix,
edition,
json_artifact_notifications,
json_unused_externs,
pretty,
}
}

View File

@ -147,6 +147,9 @@ top_level_options!(
// by the compiler.
json_artifact_notifications: bool [TRACKED],
// `true` if we're emitting a JSON blob containing the unused externs
json_unused_externs: bool [UNTRACKED],
pretty: Option<PpMode> [UNTRACKED],
}
);

View File

@ -280,9 +280,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
if needs_to_be_read {
self.borrow_expr(&discr, ty::ImmBorrow);
} else {
let closure_def_id = match discr_place.place.base {
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
_ => None,
};
self.delegate.fake_read(
discr_place.place.clone(),
FakeReadCause::ForMatchedPlace,
FakeReadCause::ForMatchedPlace(closure_def_id),
discr_place.hir_id,
);
@ -578,9 +583,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
}
fn walk_arm(&mut self, discr_place: &PlaceWithHirId<'tcx>, arm: &hir::Arm<'_>) {
let closure_def_id = match discr_place.place.base {
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
_ => None,
};
self.delegate.fake_read(
discr_place.place.clone(),
FakeReadCause::ForMatchedPlace,
FakeReadCause::ForMatchedPlace(closure_def_id),
discr_place.hir_id,
);
self.walk_pat(discr_place, &arm.pat);
@ -595,9 +605,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
/// Walks a pat that occurs in isolation (i.e., top-level of fn argument or
/// let binding, and *not* a match arm or nested pat.)
fn walk_irrefutable_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) {
let closure_def_id = match discr_place.place.base {
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
_ => None,
};
self.delegate.fake_read(
discr_place.place.clone(),
FakeReadCause::ForLet,
FakeReadCause::ForLet(closure_def_id),
discr_place.hir_id,
);
self.walk_pat(discr_place, pat);

View File

@ -128,106 +128,6 @@ impl<K, V> InternalNode<K, V> {
/// is not a separate type and has no destructor.
type BoxedNode<K, V> = NonNull<LeafNode<K, V>>;
/// The root node of an owned tree.
///
/// Note that this does not have a destructor, and must be cleaned up manually.
pub type Root<K, V> = NodeRef<marker::Owned, K, V, marker::LeafOrInternal>;
impl<K, V> Root<K, V> {
/// Returns a new owned tree, with its own root node that is initially empty.
pub fn new() -> Self {
NodeRef::new_leaf().forget_type()
}
}
impl<K, V> NodeRef<marker::Owned, K, V, marker::Leaf> {
fn new_leaf() -> Self {
Self::from_new_leaf(LeafNode::new())
}
fn from_new_leaf(leaf: Box<LeafNode<K, V>>) -> Self {
NodeRef { height: 0, node: NonNull::from(Box::leak(leaf)), _marker: PhantomData }
}
}
impl<K, V> NodeRef<marker::Owned, K, V, marker::Internal> {
fn new_internal(child: Root<K, V>) -> Self {
let mut new_node = unsafe { InternalNode::new() };
new_node.edges[0].write(child.node);
unsafe { NodeRef::from_new_internal(new_node, child.height + 1) }
}
/// # Safety
/// `height` must not be zero.
unsafe fn from_new_internal(internal: Box<InternalNode<K, V>>, height: usize) -> Self {
debug_assert!(height > 0);
let node = NonNull::from(Box::leak(internal)).cast();
let mut this = NodeRef { height, node, _marker: PhantomData };
this.borrow_mut().correct_all_childrens_parent_links();
this
}
}
impl<K, V, Type> NodeRef<marker::Owned, K, V, Type> {
/// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe
/// because the return value cannot be used to destroy the root, and there
/// cannot be other references to the tree.
pub fn borrow_mut(&mut self) -> NodeRef<marker::Mut<'_>, K, V, Type> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Slightly mutably borrows the owned root node.
pub fn borrow_valmut(&mut self) -> NodeRef<marker::ValMut<'_>, K, V, Type> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Irreversibly transitions to a reference that permits traversal and offers
/// destructive methods and little else.
pub fn into_dying(self) -> NodeRef<marker::Dying, K, V, Type> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
/// Adds a new internal node with a single edge pointing to the previous root node,
/// make that new node the root node, and return it. This increases the height by 1
/// and is the opposite of `pop_internal_level`.
pub fn push_internal_level(&mut self) -> NodeRef<marker::Mut<'_>, K, V, marker::Internal> {
super::mem::take_mut(self, |old_root| NodeRef::new_internal(old_root).forget_type());
// `self.borrow_mut()`, except that we just forgot we're internal now:
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Removes the internal root node, using its first child as the new root node.
/// As it is intended only to be called when the root node has only one child,
/// no cleanup is done on any of the keys, values and other children.
/// This decreases the height by 1 and is the opposite of `push_internal_level`.
///
/// Requires exclusive access to the `Root` object but not to the root node;
/// it will not invalidate other handles or references to the root node.
///
/// Panics if there is no internal level, i.e., if the root node is a leaf.
pub fn pop_internal_level(&mut self) {
assert!(self.height > 0);
let top = self.node;
// SAFETY: we asserted to be internal.
let internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() };
// SAFETY: we borrowed `self` exclusively and its borrow type is exclusive.
let internal_node = unsafe { &mut *NodeRef::as_internal_ptr(&internal_self) };
// SAFETY: the first edge is always initialized.
self.node = unsafe { internal_node.edges[0].assume_init_read() };
self.height -= 1;
self.clear_parent_link();
unsafe {
Global.deallocate(top.cast(), Layout::new::<InternalNode<K, V>>());
}
}
}
// N.B. `NodeRef` is always covariant in `K` and `V`, even when the `BorrowType`
// is `Mut`. This is technically wrong, but cannot result in any unsafety due to
// internal use of `NodeRef` because we stay completely generic over `K` and `V`.
@ -292,6 +192,11 @@ pub struct NodeRef<BorrowType, K, V, Type> {
_marker: PhantomData<(BorrowType, Type)>,
}
/// The root node of an owned tree.
///
/// Note that this does not have a destructor, and must be cleaned up manually.
pub type Root<K, V> = NodeRef<marker::Owned, K, V, marker::LeafOrInternal>;
impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef<marker::Immut<'a>, K, V, Type> {}
impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef<marker::Immut<'a>, K, V, Type> {
fn clone(&self) -> Self {
@ -307,6 +212,34 @@ unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send for NodeRef<marker::ValMu
unsafe impl<K: Send, V: Send, Type> Send for NodeRef<marker::Owned, K, V, Type> {}
unsafe impl<K: Send, V: Send, Type> Send for NodeRef<marker::Dying, K, V, Type> {}
impl<K, V> NodeRef<marker::Owned, K, V, marker::Leaf> {
fn new_leaf() -> Self {
Self::from_new_leaf(LeafNode::new())
}
fn from_new_leaf(leaf: Box<LeafNode<K, V>>) -> Self {
NodeRef { height: 0, node: NonNull::from(Box::leak(leaf)), _marker: PhantomData }
}
}
impl<K, V> NodeRef<marker::Owned, K, V, marker::Internal> {
fn new_internal(child: Root<K, V>) -> Self {
let mut new_node = unsafe { InternalNode::new() };
new_node.edges[0].write(child.node);
unsafe { NodeRef::from_new_internal(new_node, child.height + 1) }
}
/// # Safety
/// `height` must not be zero.
unsafe fn from_new_internal(internal: Box<InternalNode<K, V>>, height: usize) -> Self {
debug_assert!(height > 0);
let node = NonNull::from(Box::leak(internal)).cast();
let mut this = NodeRef { height, node, _marker: PhantomData };
this.borrow_mut().correct_all_childrens_parent_links();
this
}
}
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> {
/// Unpack a node reference that was packed as `NodeRef::parent`.
fn from_internal(node: NonNull<InternalNode<K, V>>, height: usize) -> Self {
@ -420,6 +353,19 @@ impl<BorrowType: marker::BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type>
}
}
impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
/// Could be a public implementation of PartialEq, but only used in this module.
fn eq(&self, other: &Self) -> bool {
let Self { node, height, _marker } = self;
if node.eq(&other.node) {
debug_assert_eq!(*height, other.height);
true
} else {
false
}
}
}
impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Immut<'a>, K, V, Type> {
/// Exposes the leaf portion of any leaf or internal node in an immutable tree.
fn into_leaf(self) -> &'a LeafNode<K, V> {
@ -461,20 +407,6 @@ impl<K, V> NodeRef<marker::Dying, K, V, marker::LeafOrInternal> {
}
}
impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> {
/// Unsafely asserts to the compiler the static information that this node is a `Leaf`.
unsafe fn cast_to_leaf_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
debug_assert!(self.height == 0);
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Unsafely asserts to the compiler the static information that this node is an `Internal`.
unsafe fn cast_to_internal_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
debug_assert!(self.height > 0);
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<'a, K, V, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
/// Temporarily takes out another, mutable reference to the same node. Beware, as
/// this method is very dangerous, doubly so since it may not immediately appear
@ -577,6 +509,22 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
}
}
impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
/// # Safety
/// Every item returned by `range` is a valid edge index for the node.
unsafe fn correct_childrens_parent_links<R: Iterator<Item = usize>>(&mut self, range: R) {
for i in range {
debug_assert!(i <= self.len());
unsafe { Handle::new_edge(self.reborrow_mut(), i) }.correct_parent_link();
}
}
fn correct_all_childrens_parent_links(&mut self) {
let len = self.len();
unsafe { self.correct_childrens_parent_links(0..=len) };
}
}
impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> {
/// Sets the node's link to its parent edge,
/// without invalidating other references to the node.
@ -596,6 +544,71 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
}
}
impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
/// Returns a new owned tree, with its own root node that is initially empty.
pub fn new() -> Self {
NodeRef::new_leaf().forget_type()
}
/// Adds a new internal node with a single edge pointing to the previous root node,
/// make that new node the root node, and return it. This increases the height by 1
/// and is the opposite of `pop_internal_level`.
pub fn push_internal_level(&mut self) -> NodeRef<marker::Mut<'_>, K, V, marker::Internal> {
super::mem::take_mut(self, |old_root| NodeRef::new_internal(old_root).forget_type());
// `self.borrow_mut()`, except that we just forgot we're internal now:
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Removes the internal root node, using its first child as the new root node.
/// As it is intended only to be called when the root node has only one child,
/// no cleanup is done on any of the keys, values and other children.
/// This decreases the height by 1 and is the opposite of `push_internal_level`.
///
/// Requires exclusive access to the `Root` object but not to the root node;
/// it will not invalidate other handles or references to the root node.
///
/// Panics if there is no internal level, i.e., if the root node is a leaf.
pub fn pop_internal_level(&mut self) {
assert!(self.height > 0);
let top = self.node;
// SAFETY: we asserted to be internal.
let internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() };
// SAFETY: we borrowed `self` exclusively and its borrow type is exclusive.
let internal_node = unsafe { &mut *NodeRef::as_internal_ptr(&internal_self) };
// SAFETY: the first edge is always initialized.
self.node = unsafe { internal_node.edges[0].assume_init_read() };
self.height -= 1;
self.clear_parent_link();
unsafe {
Global.deallocate(top.cast(), Layout::new::<InternalNode<K, V>>());
}
}
}
impl<K, V, Type> NodeRef<marker::Owned, K, V, Type> {
/// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe
/// because the return value cannot be used to destroy the root, and there
/// cannot be other references to the tree.
pub fn borrow_mut(&mut self) -> NodeRef<marker::Mut<'_>, K, V, Type> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Slightly mutably borrows the owned root node.
pub fn borrow_valmut(&mut self) -> NodeRef<marker::ValMut<'_>, K, V, Type> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Irreversibly transitions to a reference that permits traversal and offers
/// destructive methods and little else.
pub fn into_dying(self) -> NodeRef<marker::Dying, K, V, Type> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
/// Adds a key-value pair to the end of the node.
pub fn push(&mut self, key: K, val: V) {
@ -610,22 +623,6 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
}
}
impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
/// # Safety
/// Every item returned by `range` is a valid edge index for the node.
unsafe fn correct_childrens_parent_links<R: Iterator<Item = usize>>(&mut self, range: R) {
for i in range {
debug_assert!(i <= self.len());
unsafe { Handle::new_edge(self.reborrow_mut(), i) }.correct_parent_link();
}
}
fn correct_all_childrens_parent_links(&mut self) {
let len = self.len();
unsafe { self.correct_childrens_parent_links(0..=len) };
}
}
impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
/// Adds a key-value pair, and an edge to go to the right of that pair,
/// to the end of the node.
@ -645,6 +642,20 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
}
}
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Leaf> {
/// Removes any static information asserting that this node is a `Leaf` node.
pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> {
/// Removes any static information asserting that this node is an `Internal` node.
pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
/// Checks whether a node is an `Internal` node or a `Leaf` node.
pub fn force(
@ -669,6 +680,20 @@ impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
}
}
impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> {
/// Unsafely asserts to the compiler the static information that this node is a `Leaf`.
unsafe fn cast_to_leaf_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
debug_assert!(self.height == 0);
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Unsafely asserts to the compiler the static information that this node is an `Internal`.
unsafe fn cast_to_internal_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
debug_assert!(self.height > 0);
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
/// A reference to a specific key-value pair or edge within a node. The `Node` parameter
/// must be a `NodeRef`, while the `Type` can either be `KV` (signifying a handle on a key-value
/// pair) or `Edge` (signifying a handle on an edge).
@ -722,19 +747,6 @@ impl<BorrowType, K, V, NodeType> Handle<NodeRef<BorrowType, K, V, NodeType>, mar
}
}
impl<BorrowType, K, V, NodeType> NodeRef<BorrowType, K, V, NodeType> {
/// Could be a public implementation of PartialEq, but only used in this module.
fn eq(&self, other: &Self) -> bool {
let Self { node, height, _marker } = self;
if node.eq(&other.node) {
debug_assert_eq!(*height, other.height);
true
} else {
false
}
}
}
impl<BorrowType, K, V, NodeType, HandleType> PartialEq
for Handle<NodeRef<BorrowType, K, V, NodeType>, HandleType>
{
@ -754,16 +766,6 @@ impl<BorrowType, K, V, NodeType, HandleType>
}
}
impl<'a, K, V, Type> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, Type> {
/// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`.
pub unsafe fn cast_to_leaf_unchecked(
self,
) -> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, Type> {
let node = unsafe { self.node.cast_to_leaf_unchecked() };
Handle { node, idx: self.idx, _marker: PhantomData }
}
}
impl<'a, K, V, NodeType, HandleType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType>, HandleType> {
/// Temporarily takes out another, mutable handle on the same location. Beware, as
/// this method is very dangerous, doubly so since it may not immediately appear
@ -1466,20 +1468,6 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> {
}
}
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Leaf> {
/// Removes any static information asserting that this node is a `Leaf` node.
pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> {
/// Removes any static information asserting that this node is an `Internal` node.
pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> {
pub fn forget_node_type(
self,
@ -1531,6 +1519,16 @@ impl<BorrowType, K, V, Type> Handle<NodeRef<BorrowType, K, V, marker::LeafOrInte
}
}
impl<'a, K, V, Type> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, Type> {
/// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`.
pub unsafe fn cast_to_leaf_unchecked(
self,
) -> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, Type> {
let node = unsafe { self.node.cast_to_leaf_unchecked() };
Handle { node, idx: self.idx, _marker: PhantomData }
}
}
impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::Edge> {
/// Move the suffix after `self` from one node to another one. `right` must be empty.
/// The first edge of `right` remains unchanged.

View File

@ -1004,9 +1004,9 @@ fn test_from_iter_specialization_with_iterator_adapters() {
.map_while(Option::Some)
.peekable()
.skip(1)
.map(|e| std::num::NonZeroUsize::new(e));
.map(|e| if e != usize::MAX { Ok(std::num::NonZeroUsize::new(e)) } else { Err(()) });
assert_in_place_trait(&iter);
let sink = iter.collect::<Vec<_>>();
let sink = iter.collect::<Result<Vec<_>, _>>().unwrap();
let sinkptr = sink.as_ptr();
assert_eq!(srcptr, sinkptr as *const usize);
}
@ -1078,12 +1078,21 @@ fn test_from_iter_specialization_panic_during_drop_leaks() {
}
}
let mut to_free: *mut Droppable = core::ptr::null_mut();
let mut cap = 0;
let _ = std::panic::catch_unwind(AssertUnwindSafe(|| {
let v = vec![Droppable::DroppedTwice(Box::new(123)), Droppable::PanicOnDrop];
let mut v = vec![Droppable::DroppedTwice(Box::new(123)), Droppable::PanicOnDrop];
to_free = v.as_mut_ptr();
cap = v.capacity();
let _ = v.into_iter().take(0).collect::<Vec<_>>();
}));
assert_eq!(unsafe { DROP_COUNTER }, 1);
// clean up the leak to keep miri happy
unsafe {
drop(Vec::from_raw_parts(to_free, 0, cap));
}
}
#[test]

View File

@ -194,3 +194,26 @@ where
self.try_fold(init, ok(fold)).unwrap()
}
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<S: Iterator, I, E> SourceIter for ResultShunt<'_, I, E>
where
I: SourceIter<Source = S>,
{
type Source = S;
#[inline]
unsafe fn as_inner(&mut self) -> &mut S {
// SAFETY: unsafe function forwarding to unsafe function with the same requirements
unsafe { SourceIter::as_inner(&mut self.iter) }
}
}
// SAFETY: ResultShunt::next calls I::find, which has to advance `iter` in order to
// return `Some(_)`. Since `iter` has type `I: InPlaceIterable` it's guaranteed that
// at least one item will be moved out from the underlying source.
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I, T, E> InPlaceIterable for ResultShunt<'_, I, E> where
I: Iterator<Item = Result<T, E>> + InPlaceIterable
{
}

View File

@ -154,6 +154,8 @@ crate struct Options {
/// If this option is set to `true`, rustdoc will only run checks and not generate
/// documentation.
crate run_check: bool,
/// Whether doctests should emit unused externs
crate json_unused_externs: bool,
}
impl fmt::Debug for Options {
@ -352,7 +354,8 @@ impl Options {
}
let color = config::parse_color(&matches);
let (json_rendered, _artifacts) = config::parse_json(&matches);
let config::JsonConfig { json_rendered, json_unused_externs, .. } =
config::parse_json(&matches);
let error_format = config::parse_error_format(&matches, color, json_rendered);
let codegen_options = build_codegen_options(matches, error_format);
@ -507,7 +510,6 @@ impl Options {
let edition = config::parse_crate_edition(&matches);
let mut id_map = html::markdown::IdMap::new();
id_map.populate(&html::render::INITIAL_IDS);
let external_html = match ExternalHtml::load(
&matches.opt_strs("html-in-header"),
&matches.opt_strs("html-before-content"),
@ -687,6 +689,7 @@ impl Options {
},
crate_name,
output_format,
json_unused_externs,
})
}

View File

@ -1,5 +1,5 @@
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::Lrc;
use rustc_errors::{ColorConfig, ErrorReported};
use rustc_hir as hir;
@ -23,6 +23,8 @@ use std::panic;
use std::path::PathBuf;
use std::process::{self, Command, Stdio};
use std::str;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use crate::clean::Attributes;
use crate::config::Options;
@ -104,8 +106,10 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
let mut test_args = options.test_args.clone();
let display_warnings = options.display_warnings;
let externs = options.externs.clone();
let json_unused_externs = options.json_unused_externs;
let tests = interface::run_compiler(config, |compiler| {
let res = interface::run_compiler(config, |compiler| {
compiler.enter(|queries| {
let _lower_to_hir = queries.lower_to_hir()?;
@ -151,12 +155,15 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
});
compiler.session().abort_if_errors();
let ret: Result<_, ErrorReported> = Ok(collector.tests);
let unused_extern_reports = collector.unused_extern_reports.clone();
let compiling_test_count = collector.compiling_test_count.load(Ordering::SeqCst);
let ret: Result<_, ErrorReported> =
Ok((collector.tests, unused_extern_reports, compiling_test_count));
ret
})
});
let tests = match tests {
Ok(tests) => tests,
let (tests, unused_extern_reports, compiling_test_count) = match res {
Ok(res) => res,
Err(ErrorReported) => return Err(ErrorReported),
};
@ -168,6 +175,44 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
Some(testing::Options::new().display_output(display_warnings)),
);
// Collect and warn about unused externs, but only if we've gotten
// reports for each doctest
if json_unused_externs {
let unused_extern_reports: Vec<_> =
std::mem::take(&mut unused_extern_reports.lock().unwrap());
if unused_extern_reports.len() == compiling_test_count {
let extern_names = externs.iter().map(|(name, _)| name).collect::<FxHashSet<&String>>();
let mut unused_extern_names = unused_extern_reports
.iter()
.map(|uexts| uexts.unused_extern_names.iter().collect::<FxHashSet<&String>>())
.fold(extern_names, |uextsa, uextsb| {
uextsa.intersection(&uextsb).map(|v| *v).collect::<FxHashSet<&String>>()
})
.iter()
.map(|v| (*v).clone())
.collect::<Vec<String>>();
unused_extern_names.sort();
// Take the most severe lint level
let lint_level = unused_extern_reports
.iter()
.map(|uexts| uexts.lint_level.as_str())
.max_by_key(|v| match *v {
"warn" => 1,
"deny" => 2,
"forbid" => 3,
// The allow lint level is not expected,
// as if allow is specified, no message
// is to be emitted.
v => unreachable!("Invalid lint level '{}'", v),
})
.unwrap_or("warn")
.to_string();
let uext = UnusedExterns { lint_level, unused_extern_names };
let unused_extern_json = serde_json::to_string(&uext).unwrap();
eprintln!("{}", unused_extern_json);
}
}
Ok(())
}
@ -235,6 +280,18 @@ impl DirState {
}
}
// NOTE: Keep this in sync with the equivalent structs in rustc
// and cargo.
// We could unify this struct the one in rustc but they have different
// ownership semantics, so doing so would create wasteful allocations.
#[derive(serde::Serialize, serde::Deserialize)]
struct UnusedExterns {
/// Lint level of the unused_crate_dependencies lint
lint_level: String,
/// List of unused externs by their names.
unused_extern_names: Vec<String>,
}
fn run_test(
test: &str,
cratename: &str,
@ -253,6 +310,7 @@ fn run_test(
outdir: DirState,
path: PathBuf,
test_id: &str,
report_unused_externs: impl Fn(UnusedExterns),
) -> Result<(), TestFailure> {
let (test, line_offset, supports_color) =
make_test(test, Some(cratename), as_test_harness, opts, edition, Some(test_id));
@ -278,6 +336,12 @@ fn run_test(
if as_test_harness {
compiler.arg("--test");
}
if options.json_unused_externs && !compile_fail {
compiler.arg("--error-format=json");
compiler.arg("--json").arg("unused-externs");
compiler.arg("-Z").arg("unstable-options");
compiler.arg("-W").arg("unused_crate_dependencies");
}
for lib_str in &options.lib_strs {
compiler.arg("-L").arg(&lib_str);
}
@ -337,7 +401,26 @@ fn run_test(
eprint!("{}", self.0);
}
}
let out = str::from_utf8(&output.stderr).unwrap();
let mut out_lines = str::from_utf8(&output.stderr)
.unwrap()
.lines()
.filter(|l| {
if let Ok(uext) = serde_json::from_str::<UnusedExterns>(l) {
report_unused_externs(uext);
false
} else {
true
}
})
.collect::<Vec<_>>();
// Add a \n to the end to properly terminate the last line,
// but only if there was output to be printed
if out_lines.len() > 0 {
out_lines.push("");
}
let out = out_lines.join("\n");
let _bomb = Bomb(&out);
match (output.status.success(), compile_fail) {
(true, true) => {
@ -721,6 +804,8 @@ crate struct Collector {
source_map: Option<Lrc<SourceMap>>,
filename: Option<PathBuf>,
visited_tests: FxHashMap<(String, usize), usize>,
unused_extern_reports: Arc<Mutex<Vec<UnusedExterns>>>,
compiling_test_count: AtomicUsize,
}
impl Collector {
@ -745,6 +830,8 @@ impl Collector {
source_map,
filename,
visited_tests: FxHashMap::default(),
unused_extern_reports: Default::default(),
compiling_test_count: AtomicUsize::new(0),
}
}
@ -791,6 +878,10 @@ impl Tester for Collector {
let runtool_args = self.options.runtool_args.clone();
let target = self.options.target.clone();
let target_str = target.to_string();
let unused_externs = self.unused_extern_reports.clone();
if !config.compile_fail {
self.compiling_test_count.fetch_add(1, Ordering::SeqCst);
}
// FIXME(#44940): if doctests ever support path remapping, then this filename
// needs to be the result of `SourceMap::span_to_unmapped_path`.
@ -846,6 +937,9 @@ impl Tester for Collector {
test_type: testing::TestType::DocTest,
},
testfn: testing::DynTestFn(box move || {
let report_unused_externs = |uext| {
unused_externs.lock().unwrap().push(uext);
};
let res = run_test(
&test,
&cratename,
@ -864,6 +958,7 @@ impl Tester for Collector {
outdir,
path,
&test_id,
report_unused_externs,
);
if let Err(err) = res {

View File

@ -189,7 +189,9 @@ impl<'a> Classifier<'a> {
// leading identifier.
TokenKind::Bang if self.in_macro => {
self.in_macro = false;
Class::Macro
sink(Highlight::Token { text, class: None });
sink(Highlight::ExitSpan);
return;
}
// Assume that '&' or '*' is the reference or dereference operator
@ -298,7 +300,9 @@ impl<'a> Classifier<'a> {
},
TokenKind::Ident | TokenKind::RawIdent if lookahead == Some(TokenKind::Bang) => {
self.in_macro = true;
Class::Macro
sink(Highlight::EnterSpan { class: Class::Macro });
sink(Highlight::Token { text, class: None });
return;
}
TokenKind::Ident => match text {
"ref" | "mut" => Class::RefKeyWord,

View File

@ -1,3 +1,3 @@
<span class="kw">pub</span> <span class="kw">fn</span> <span class="ident">foo</span>() {
<span class="macro">println</span><span class="macro">!</span>(<span class="string">&quot;foo&quot;</span>);
<span class="macro">println!</span>(<span class="string">&quot;foo&quot;</span>);
}

View File

@ -17,11 +17,11 @@
<span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">foo</span>;
<span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="op">&amp;&amp;</span><span class="ident">foo</span>;
<span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="kw-2">*</span><span class="ident">foo</span>;
<span class="macro">mac</span><span class="macro">!</span>(<span class="ident">foo</span>, <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">bar</span>);
<span class="macro">assert</span><span class="macro">!</span>(<span class="self">self</span>.<span class="ident">length</span> <span class="op">&lt;</span> <span class="ident">N</span> <span class="op">&amp;&amp;</span> <span class="ident">index</span> <span class="op">&lt;</span><span class="op">=</span> <span class="self">self</span>.<span class="ident">length</span>);
<span class="macro">mac!</span>(<span class="ident">foo</span>, <span class="kw-2">&amp;</span><span class="kw-2">mut</span> <span class="ident">bar</span>);
<span class="macro">assert!</span>(<span class="self">self</span>.<span class="ident">length</span> <span class="op">&lt;</span> <span class="ident">N</span> <span class="op">&amp;&amp;</span> <span class="ident">index</span> <span class="op">&lt;</span><span class="op">=</span> <span class="self">self</span>.<span class="ident">length</span>);
}
<span class="macro">macro_rules</span><span class="macro">!</span> <span class="ident">bar</span> {
<span class="macro">macro_rules!</span> <span class="ident">bar</span> {
(<span class="macro-nonterminal">$</span><span class="macro-nonterminal">foo</span>:<span class="ident">tt</span>) <span class="op">=</span><span class="op">&gt;</span> {};
}
</code></pre>

View File

@ -1356,6 +1356,9 @@ fn init_id_map() -> FxHashMap<String, usize> {
map.insert("rustdoc-vars".to_owned(), 1);
map.insert("sidebar-vars".to_owned(), 1);
map.insert("copy-path".to_owned(), 1);
map.insert("help".to_owned(), 1);
map.insert("TOC".to_owned(), 1);
map.insert("render-detail".to_owned(), 1);
// This is the list of IDs used by rustdoc sections.
map.insert("fields".to_owned(), 1);
map.insert("variants".to_owned(), 1);
@ -1365,6 +1368,12 @@ fn init_id_map() -> FxHashMap<String, usize> {
map.insert("trait-implementations".to_owned(), 1);
map.insert("synthetic-implementations".to_owned(), 1);
map.insert("blanket-implementations".to_owned(), 1);
map.insert("associated-types".to_owned(), 1);
map.insert("associated-const".to_owned(), 1);
map.insert("required-methods".to_owned(), 1);
map.insert("provided-methods".to_owned(), 1);
map.insert("implementors".to_owned(), 1);
map.insert("synthetic-implementors".to_owned(), 1);
map
}
@ -1373,12 +1382,6 @@ impl IdMap {
IdMap { map: init_id_map() }
}
crate fn populate<I: IntoIterator<Item = S>, S: AsRef<str> + ToString>(&mut self, ids: I) {
for id in ids {
let _ = self.derive(id);
}
}
crate fn derive<S: AsRef<str> + ToString>(&mut self, candidate: S) -> String {
let id = match self.map.get_mut(candidate.as_ref()) {
None => candidate.to_string(),

View File

@ -18,7 +18,7 @@ use super::print_item::{full_path, item_path, print_item};
use super::write_shared::write_shared;
use super::{
print_sidebar, settings, AllTypes, NameDoc, SharedContext, StylePath, BASIC_KEYWORDS,
CURRENT_DEPTH, INITIAL_IDS,
CURRENT_DEPTH,
};
use crate::clean::{self, AttributesExt};
@ -423,14 +423,11 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
}
fn make_child_renderer(&self) -> Self {
let mut id_map = IdMap::new();
id_map.populate(&INITIAL_IDS);
Self {
current: self.current.clone(),
dst: self.dst.clone(),
render_redirect_pages: self.render_redirect_pages,
id_map: RefCell::new(id_map),
id_map: RefCell::new(IdMap::new()),
deref_id_map: RefCell::new(FxHashMap::default()),
shared: Rc::clone(&self.shared),
cache: Rc::clone(&self.cache),

View File

@ -283,24 +283,6 @@ crate struct StylePath {
thread_local!(crate static CURRENT_DEPTH: Cell<usize> = Cell::new(0));
crate const INITIAL_IDS: [&'static str; 15] = [
"main",
"search",
"help",
"TOC",
"render-detail",
"associated-types",
"associated-const",
"required-methods",
"provided-methods",
"implementors",
"synthetic-implementors",
"implementors-list",
"synthetic-implementors-list",
"methods",
"implementations",
];
fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
if let Some(l) = cx.src_href(item) {
write!(buf, "<a class=\"srclink\" href=\"{}\" title=\"goto source code\">[src]</a>", l)

View File

@ -130,12 +130,12 @@ fn address_of_reborrow() -> () {
StorageLive(_2); // scope 0 at $DIR/address-of.rs:4:14: 4:21
_2 = [const 0_i32; 10]; // scope 0 at $DIR/address-of.rs:4:14: 4:21
_1 = &_2; // scope 0 at $DIR/address-of.rs:4:13: 4:21
FakeRead(ForLet, _1); // scope 0 at $DIR/address-of.rs:4:9: 4:10
FakeRead(ForLet(None), _1); // scope 0 at $DIR/address-of.rs:4:9: 4:10
StorageLive(_3); // scope 1 at $DIR/address-of.rs:5:9: 5:14
StorageLive(_4); // scope 1 at $DIR/address-of.rs:5:22: 5:29
_4 = [const 0_i32; 10]; // scope 1 at $DIR/address-of.rs:5:22: 5:29
_3 = &mut _4; // scope 1 at $DIR/address-of.rs:5:17: 5:29
FakeRead(ForLet, _3); // scope 1 at $DIR/address-of.rs:5:9: 5:14
FakeRead(ForLet(None), _3); // scope 1 at $DIR/address-of.rs:5:9: 5:14
StorageLive(_5); // scope 2 at $DIR/address-of.rs:7:5: 7:18
StorageLive(_6); // scope 2 at $DIR/address-of.rs:7:5: 7:18
_6 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:7:5: 7:6
@ -170,25 +170,25 @@ fn address_of_reborrow() -> () {
StorageDead(_13); // scope 2 at $DIR/address-of.rs:11:20: 11:21
StorageLive(_15); // scope 2 at $DIR/address-of.rs:13:9: 13:10
_15 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:13:23: 13:24
FakeRead(ForLet, _15); // scope 2 at $DIR/address-of.rs:13:9: 13:10
FakeRead(ForLet(None), _15); // scope 2 at $DIR/address-of.rs:13:9: 13:10
AscribeUserType(_15, o, UserTypeProjection { base: UserType(3), projs: [] }); // scope 2 at $DIR/address-of.rs:13:12: 13:20
StorageLive(_16); // scope 3 at $DIR/address-of.rs:14:9: 14:10
_16 = &raw const (*_1); // scope 3 at $DIR/address-of.rs:14:31: 14:32
FakeRead(ForLet, _16); // scope 3 at $DIR/address-of.rs:14:9: 14:10
FakeRead(ForLet(None), _16); // scope 3 at $DIR/address-of.rs:14:9: 14:10
AscribeUserType(_16, o, UserTypeProjection { base: UserType(5), projs: [] }); // scope 3 at $DIR/address-of.rs:14:12: 14:28
StorageLive(_17); // scope 4 at $DIR/address-of.rs:15:9: 15:10
StorageLive(_18); // scope 4 at $DIR/address-of.rs:15:30: 15:31
_18 = &raw const (*_1); // scope 4 at $DIR/address-of.rs:15:30: 15:31
_17 = move _18 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 4 at $DIR/address-of.rs:15:30: 15:31
StorageDead(_18); // scope 4 at $DIR/address-of.rs:15:30: 15:31
FakeRead(ForLet, _17); // scope 4 at $DIR/address-of.rs:15:9: 15:10
FakeRead(ForLet(None), _17); // scope 4 at $DIR/address-of.rs:15:9: 15:10
AscribeUserType(_17, o, UserTypeProjection { base: UserType(7), projs: [] }); // scope 4 at $DIR/address-of.rs:15:12: 15:27
StorageLive(_19); // scope 5 at $DIR/address-of.rs:16:9: 16:10
StorageLive(_20); // scope 5 at $DIR/address-of.rs:16:27: 16:28
_20 = &raw const (*_1); // scope 5 at $DIR/address-of.rs:16:27: 16:28
_19 = move _20 as *const [i32] (Pointer(Unsize)); // scope 5 at $DIR/address-of.rs:16:27: 16:28
StorageDead(_20); // scope 5 at $DIR/address-of.rs:16:27: 16:28
FakeRead(ForLet, _19); // scope 5 at $DIR/address-of.rs:16:9: 16:10
FakeRead(ForLet(None), _19); // scope 5 at $DIR/address-of.rs:16:9: 16:10
AscribeUserType(_19, o, UserTypeProjection { base: UserType(9), projs: [] }); // scope 5 at $DIR/address-of.rs:16:12: 16:24
StorageLive(_21); // scope 6 at $DIR/address-of.rs:18:5: 18:18
StorageLive(_22); // scope 6 at $DIR/address-of.rs:18:5: 18:18
@ -218,25 +218,25 @@ fn address_of_reborrow() -> () {
StorageDead(_27); // scope 6 at $DIR/address-of.rs:21:22: 21:23
StorageLive(_29); // scope 6 at $DIR/address-of.rs:23:9: 23:10
_29 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:23:23: 23:24
FakeRead(ForLet, _29); // scope 6 at $DIR/address-of.rs:23:9: 23:10
FakeRead(ForLet(None), _29); // scope 6 at $DIR/address-of.rs:23:9: 23:10
AscribeUserType(_29, o, UserTypeProjection { base: UserType(13), projs: [] }); // scope 6 at $DIR/address-of.rs:23:12: 23:20
StorageLive(_30); // scope 7 at $DIR/address-of.rs:24:9: 24:10
_30 = &raw const (*_3); // scope 7 at $DIR/address-of.rs:24:31: 24:32
FakeRead(ForLet, _30); // scope 7 at $DIR/address-of.rs:24:9: 24:10
FakeRead(ForLet(None), _30); // scope 7 at $DIR/address-of.rs:24:9: 24:10
AscribeUserType(_30, o, UserTypeProjection { base: UserType(15), projs: [] }); // scope 7 at $DIR/address-of.rs:24:12: 24:28
StorageLive(_31); // scope 8 at $DIR/address-of.rs:25:9: 25:10
StorageLive(_32); // scope 8 at $DIR/address-of.rs:25:30: 25:31
_32 = &raw const (*_3); // scope 8 at $DIR/address-of.rs:25:30: 25:31
_31 = move _32 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 8 at $DIR/address-of.rs:25:30: 25:31
StorageDead(_32); // scope 8 at $DIR/address-of.rs:25:30: 25:31
FakeRead(ForLet, _31); // scope 8 at $DIR/address-of.rs:25:9: 25:10
FakeRead(ForLet(None), _31); // scope 8 at $DIR/address-of.rs:25:9: 25:10
AscribeUserType(_31, o, UserTypeProjection { base: UserType(17), projs: [] }); // scope 8 at $DIR/address-of.rs:25:12: 25:27
StorageLive(_33); // scope 9 at $DIR/address-of.rs:26:9: 26:10
StorageLive(_34); // scope 9 at $DIR/address-of.rs:26:27: 26:28
_34 = &raw const (*_3); // scope 9 at $DIR/address-of.rs:26:27: 26:28
_33 = move _34 as *const [i32] (Pointer(Unsize)); // scope 9 at $DIR/address-of.rs:26:27: 26:28
StorageDead(_34); // scope 9 at $DIR/address-of.rs:26:27: 26:28
FakeRead(ForLet, _33); // scope 9 at $DIR/address-of.rs:26:9: 26:10
FakeRead(ForLet(None), _33); // scope 9 at $DIR/address-of.rs:26:9: 26:10
AscribeUserType(_33, o, UserTypeProjection { base: UserType(19), projs: [] }); // scope 9 at $DIR/address-of.rs:26:12: 26:24
StorageLive(_35); // scope 10 at $DIR/address-of.rs:28:5: 28:16
StorageLive(_36); // scope 10 at $DIR/address-of.rs:28:5: 28:16
@ -266,25 +266,25 @@ fn address_of_reborrow() -> () {
StorageDead(_41); // scope 10 at $DIR/address-of.rs:31:20: 31:21
StorageLive(_43); // scope 10 at $DIR/address-of.rs:33:9: 33:10
_43 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:33:21: 33:22
FakeRead(ForLet, _43); // scope 10 at $DIR/address-of.rs:33:9: 33:10
FakeRead(ForLet(None), _43); // scope 10 at $DIR/address-of.rs:33:9: 33:10
AscribeUserType(_43, o, UserTypeProjection { base: UserType(23), projs: [] }); // scope 10 at $DIR/address-of.rs:33:12: 33:18
StorageLive(_44); // scope 11 at $DIR/address-of.rs:34:9: 34:10
_44 = &raw mut (*_3); // scope 11 at $DIR/address-of.rs:34:29: 34:30
FakeRead(ForLet, _44); // scope 11 at $DIR/address-of.rs:34:9: 34:10
FakeRead(ForLet(None), _44); // scope 11 at $DIR/address-of.rs:34:9: 34:10
AscribeUserType(_44, o, UserTypeProjection { base: UserType(25), projs: [] }); // scope 11 at $DIR/address-of.rs:34:12: 34:26
StorageLive(_45); // scope 12 at $DIR/address-of.rs:35:9: 35:10
StorageLive(_46); // scope 12 at $DIR/address-of.rs:35:28: 35:29
_46 = &raw mut (*_3); // scope 12 at $DIR/address-of.rs:35:28: 35:29
_45 = move _46 as *mut dyn std::marker::Send (Pointer(Unsize)); // scope 12 at $DIR/address-of.rs:35:28: 35:29
StorageDead(_46); // scope 12 at $DIR/address-of.rs:35:28: 35:29
FakeRead(ForLet, _45); // scope 12 at $DIR/address-of.rs:35:9: 35:10
FakeRead(ForLet(None), _45); // scope 12 at $DIR/address-of.rs:35:9: 35:10
AscribeUserType(_45, o, UserTypeProjection { base: UserType(27), projs: [] }); // scope 12 at $DIR/address-of.rs:35:12: 35:25
StorageLive(_47); // scope 13 at $DIR/address-of.rs:36:9: 36:10
StorageLive(_48); // scope 13 at $DIR/address-of.rs:36:25: 36:26
_48 = &raw mut (*_3); // scope 13 at $DIR/address-of.rs:36:25: 36:26
_47 = move _48 as *mut [i32] (Pointer(Unsize)); // scope 13 at $DIR/address-of.rs:36:25: 36:26
StorageDead(_48); // scope 13 at $DIR/address-of.rs:36:25: 36:26
FakeRead(ForLet, _47); // scope 13 at $DIR/address-of.rs:36:9: 36:10
FakeRead(ForLet(None), _47); // scope 13 at $DIR/address-of.rs:36:9: 36:10
AscribeUserType(_47, o, UserTypeProjection { base: UserType(29), projs: [] }); // scope 13 at $DIR/address-of.rs:36:12: 36:22
_0 = const (); // scope 0 at $DIR/address-of.rs:3:26: 37:2
StorageDead(_47); // scope 13 at $DIR/address-of.rs:37:1: 37:2

View File

@ -24,19 +24,19 @@ fn borrow_and_cast(_1: i32) -> () {
StorageLive(_3); // scope 0 at $DIR/address-of.rs:42:13: 42:15
_3 = &_1; // scope 0 at $DIR/address-of.rs:42:13: 42:15
_2 = &raw const (*_3); // scope 0 at $DIR/address-of.rs:42:13: 42:15
FakeRead(ForLet, _2); // scope 0 at $DIR/address-of.rs:42:9: 42:10
FakeRead(ForLet(None), _2); // scope 0 at $DIR/address-of.rs:42:9: 42:10
StorageDead(_3); // scope 0 at $DIR/address-of.rs:42:29: 42:30
StorageLive(_4); // scope 1 at $DIR/address-of.rs:43:9: 43:10
StorageLive(_5); // scope 1 at $DIR/address-of.rs:43:13: 43:19
_5 = &mut _1; // scope 1 at $DIR/address-of.rs:43:13: 43:19
_4 = &raw const (*_5); // scope 1 at $DIR/address-of.rs:43:13: 43:19
FakeRead(ForLet, _4); // scope 1 at $DIR/address-of.rs:43:9: 43:10
FakeRead(ForLet(None), _4); // scope 1 at $DIR/address-of.rs:43:9: 43:10
StorageDead(_5); // scope 1 at $DIR/address-of.rs:43:33: 43:34
StorageLive(_6); // scope 2 at $DIR/address-of.rs:44:9: 44:10
StorageLive(_7); // scope 2 at $DIR/address-of.rs:44:13: 44:19
_7 = &mut _1; // scope 2 at $DIR/address-of.rs:44:13: 44:19
_6 = &raw mut (*_7); // scope 2 at $DIR/address-of.rs:44:13: 44:19
FakeRead(ForLet, _6); // scope 2 at $DIR/address-of.rs:44:9: 44:10
FakeRead(ForLet(None), _6); // scope 2 at $DIR/address-of.rs:44:9: 44:10
StorageDead(_7); // scope 2 at $DIR/address-of.rs:44:31: 44:32
_0 = const (); // scope 0 at $DIR/address-of.rs:41:32: 45:2
StorageDead(_6); // scope 2 at $DIR/address-of.rs:45:1: 45:2

View File

@ -28,7 +28,7 @@ fn main() -> () {
bb0: {
StorageLive(_1); // scope 0 at $DIR/basic_assignment.rs:11:9: 11:17
_1 = const false; // scope 0 at $DIR/basic_assignment.rs:11:20: 11:25
FakeRead(ForLet, _1); // scope 0 at $DIR/basic_assignment.rs:11:9: 11:17
FakeRead(ForLet(None), _1); // scope 0 at $DIR/basic_assignment.rs:11:9: 11:17
StorageLive(_2); // scope 1 at $DIR/basic_assignment.rs:12:9: 12:17
StorageLive(_3); // scope 2 at $DIR/basic_assignment.rs:16:16: 16:24
_3 = _1; // scope 2 at $DIR/basic_assignment.rs:16:16: 16:24
@ -36,7 +36,7 @@ fn main() -> () {
StorageDead(_3); // scope 2 at $DIR/basic_assignment.rs:16:23: 16:24
StorageLive(_4); // scope 2 at $DIR/basic_assignment.rs:18:9: 18:15
_4 = Option::<Box<u32>>::None; // scope 2 at $DIR/basic_assignment.rs:18:36: 18:40
FakeRead(ForLet, _4); // scope 2 at $DIR/basic_assignment.rs:18:9: 18:15
FakeRead(ForLet(None), _4); // scope 2 at $DIR/basic_assignment.rs:18:9: 18:15
AscribeUserType(_4, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 2 at $DIR/basic_assignment.rs:18:17: 18:33
StorageLive(_5); // scope 3 at $DIR/basic_assignment.rs:19:9: 19:15
StorageLive(_6); // scope 4 at $DIR/basic_assignment.rs:23:14: 23:20

View File

@ -18,7 +18,7 @@ fn match_tuple(_1: (u32, bool, Option<i32>, u32)) -> u32 {
}
bb0: {
FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/exponential-or.rs:5:11: 5:12
FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/exponential-or.rs:5:11: 5:12
switchInt((_1.0: u32)) -> [1_u32: bb2, 4_u32: bb2, otherwise: bb1]; // scope 0 at $DIR/exponential-or.rs:6:15: 6:16
}

View File

@ -14,7 +14,7 @@ fn main() -> () {
bb0: {
StorageLive(_1); // scope 0 at $DIR/issue-38669.rs:5:9: 5:25
_1 = const false; // scope 0 at $DIR/issue-38669.rs:5:28: 5:33
FakeRead(ForLet, _1); // scope 0 at $DIR/issue-38669.rs:5:9: 5:25
FakeRead(ForLet(None), _1); // scope 0 at $DIR/issue-38669.rs:5:9: 5:25
goto -> bb1; // scope 1 at $DIR/issue-38669.rs:6:5: 11:6
}

View File

@ -24,7 +24,7 @@ fn main() -> () {
StorageLive(_2); // scope 0 at $DIR/issue-49232.rs:7:13: 7:19
StorageLive(_3); // scope 0 at $DIR/issue-49232.rs:8:19: 8:23
_3 = const true; // scope 0 at $DIR/issue-49232.rs:8:19: 8:23
FakeRead(ForMatchedPlace, _3); // scope 0 at $DIR/issue-49232.rs:8:19: 8:23
FakeRead(ForMatchedPlace(None), _3); // scope 0 at $DIR/issue-49232.rs:8:19: 8:23
switchInt(_3) -> [false: bb3, otherwise: bb4]; // scope 0 at $DIR/issue-49232.rs:9:17: 9:22
}
@ -51,7 +51,7 @@ fn main() -> () {
}
bb8: {
FakeRead(ForLet, _2); // scope 0 at $DIR/issue-49232.rs:7:13: 7:19
FakeRead(ForLet(None), _2); // scope 0 at $DIR/issue-49232.rs:7:13: 7:19
StorageDead(_3); // scope 0 at $DIR/issue-49232.rs:12:10: 12:11
StorageLive(_5); // scope 1 at $DIR/issue-49232.rs:13:9: 13:22
StorageLive(_6); // scope 1 at $DIR/issue-49232.rs:13:14: 13:21

View File

@ -38,7 +38,7 @@ fn main() -> () {
_2 = [move _3, move _4]; // scope 1 at $DIR/issue-72181.rs:26:13: 26:43
StorageDead(_4); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43
StorageDead(_3); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43
FakeRead(ForLet, _2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10
FakeRead(ForLet(None), _2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10
StorageLive(_5); // scope 2 at $DIR/issue-72181.rs:27:13: 27:30
StorageLive(_6); // scope 4 at $DIR/issue-72181.rs:27:24: 27:25
_6 = const 0_usize; // scope 4 at $DIR/issue-72181.rs:27:24: 27:25

View File

@ -38,7 +38,7 @@ fn main() -> () {
_2 = [move _3, move _4]; // scope 1 at $DIR/issue-72181.rs:26:13: 26:43
StorageDead(_4); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43
StorageDead(_3); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43
FakeRead(ForLet, _2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10
FakeRead(ForLet(None), _2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10
StorageLive(_5); // scope 2 at $DIR/issue-72181.rs:27:13: 27:30
StorageLive(_6); // scope 4 at $DIR/issue-72181.rs:27:24: 27:25
_6 = const 0_usize; // scope 4 at $DIR/issue-72181.rs:27:24: 27:25

View File

@ -9,7 +9,7 @@ fn f(_1: Void) -> ! {
bb0: {
StorageLive(_2); // scope 0 at $DIR/issue-72181-1.rs:10:20: 12:2
StorageLive(_3); // scope 0 at $DIR/issue-72181-1.rs:11:5: 11:15
FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/issue-72181-1.rs:11:11: 11:12
FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/issue-72181-1.rs:11:11: 11:12
unreachable; // scope 0 at $DIR/issue-72181-1.rs:11:11: 11:12
}

View File

@ -29,7 +29,7 @@ fn main() -> () {
bb1: {
StorageDead(_3); // scope 2 at $DIR/issue-72181-1.rs:17:43: 17:44
FakeRead(ForLet, _2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
FakeRead(ForLet(None), _2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
AscribeUserType(_2, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/issue-72181-1.rs:16:12: 16:16
StorageLive(_4); // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9
StorageLive(_5); // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8

View File

@ -41,7 +41,7 @@ fn main() -> () {
bb4: {
StorageLive(_6); // scope 0 at $DIR/loop_test.rs:14:13: 14:14
_6 = const 1_i32; // scope 0 at $DIR/loop_test.rs:14:17: 14:18
FakeRead(ForLet, _6); // scope 0 at $DIR/loop_test.rs:14:13: 14:14
FakeRead(ForLet(None), _6); // scope 0 at $DIR/loop_test.rs:14:13: 14:14
StorageDead(_6); // scope 0 at $DIR/loop_test.rs:16:5: 16:6
goto -> bb3; // scope 0 at $DIR/loop_test.rs:1:1: 1:1
}

View File

@ -31,7 +31,7 @@
}
bb0: {
- FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match-arm-scopes.rs:14:11: 14:16
- FakeRead(ForMatchedPlace(None), _2); // scope 0 at $DIR/match-arm-scopes.rs:14:11: 14:16
- switchInt((_2.0: bool)) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/match-arm-scopes.rs:15:10: 15:15
+ switchInt((_2.0: bool)) -> [false: bb5, otherwise: bb1]; // scope 0 at $DIR/match-arm-scopes.rs:15:10: 15:15
}

View File

@ -27,7 +27,7 @@ fn full_tested_match() -> () {
StorageLive(_1); // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6
StorageLive(_2); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27
_2 = Option::<i32>::Some(const 42_i32); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27
FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27
FakeRead(ForMatchedPlace(None), _2); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27
_3 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16
switchInt(move _3) -> [0_isize: bb1, 1_isize: bb2, otherwise: bb4]; // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16
}

View File

@ -26,7 +26,7 @@ fn full_tested_match2() -> () {
StorageLive(_1); // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6
StorageLive(_2); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27
_2 = Option::<i32>::Some(const 42_i32); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27
FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27
FakeRead(ForMatchedPlace(None), _2); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27
_3 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16
switchInt(move _3) -> [0_isize: bb1, 1_isize: bb2, otherwise: bb4]; // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16
}

View File

@ -37,7 +37,7 @@ fn main() -> () {
StorageLive(_1); // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6
StorageLive(_2); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26
_2 = Option::<i32>::Some(const 1_i32); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26
FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26
FakeRead(ForMatchedPlace(None), _2); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26
_4 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17
switchInt(move _4) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17
}

View File

@ -21,12 +21,12 @@ fn main() -> () {
bb0: {
StorageLive(_1); // scope 0 at $DIR/match_test.rs:7:9: 7:10
_1 = const 3_i32; // scope 0 at $DIR/match_test.rs:7:13: 7:14
FakeRead(ForLet, _1); // scope 0 at $DIR/match_test.rs:7:9: 7:10
FakeRead(ForLet(None), _1); // scope 0 at $DIR/match_test.rs:7:9: 7:10
StorageLive(_2); // scope 1 at $DIR/match_test.rs:8:9: 8:10
_2 = const true; // scope 1 at $DIR/match_test.rs:8:13: 8:17
FakeRead(ForLet, _2); // scope 1 at $DIR/match_test.rs:8:9: 8:10
FakeRead(ForLet(None), _2); // scope 1 at $DIR/match_test.rs:8:9: 8:10
StorageLive(_3); // scope 2 at $DIR/match_test.rs:12:5: 17:6
FakeRead(ForMatchedPlace, _1); // scope 2 at $DIR/match_test.rs:12:11: 12:12
FakeRead(ForMatchedPlace(None), _1); // scope 2 at $DIR/match_test.rs:12:11: 12:12
_6 = Le(const 0_i32, _1); // scope 2 at $DIR/match_test.rs:13:9: 13:14
switchInt(move _6) -> [false: bb4, otherwise: bb1]; // scope 2 at $DIR/match_test.rs:13:9: 13:14
}

View File

@ -46,7 +46,7 @@ fn main() -> () {
bb0: {
StorageLive(_1); // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14
_1 = [const Const(Value(Scalar(0x00000001)): usize), const Const(Value(Scalar(0x00000002)): usize), const Const(Value(Scalar(0x00000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26
FakeRead(ForLet, _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14
FakeRead(ForLet(None), _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14
StorageLive(_2); // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10
StorageLive(_3); // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17
_3 = const Const(Value(Scalar(0x00000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17
@ -57,10 +57,10 @@ fn main() -> () {
bb1: {
_2 = &'_#3r _1[_3]; // bb1[0]: scope 1 at $DIR/region-subtyping-basic.rs:18:13: 18:18
FakeRead(ForLet, _2); // bb1[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10
FakeRead(ForLet(None), _2); // bb1[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10
StorageLive(_6); // bb1[2]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10
_6 = _2; // bb1[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14
FakeRead(ForLet, _6); // bb1[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10
FakeRead(ForLet(None), _6); // bb1[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10
StorageLive(_7); // bb1[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12
_7 = const Const(Value(Scalar(0x01)): bool); // bb1[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12
switchInt(move _7) -> [Const(Value(Scalar(0x00)): bool): bb3, otherwise: bb2]; // bb1[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6

View File

@ -46,7 +46,7 @@ fn main() -> () {
bb0: {
StorageLive(_1); // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14
_1 = [const Const(Value(Scalar(0x0000000000000001)): usize), const Const(Value(Scalar(0x0000000000000002)): usize), const Const(Value(Scalar(0x0000000000000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26
FakeRead(ForLet, _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14
FakeRead(ForLet(None), _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14
StorageLive(_2); // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10
StorageLive(_3); // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17
_3 = const Const(Value(Scalar(0x0000000000000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17
@ -57,10 +57,10 @@ fn main() -> () {
bb1: {
_2 = &'_#3r _1[_3]; // bb1[0]: scope 1 at $DIR/region-subtyping-basic.rs:18:13: 18:18
FakeRead(ForLet, _2); // bb1[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10
FakeRead(ForLet(None), _2); // bb1[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10
StorageLive(_6); // bb1[2]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10
_6 = _2; // bb1[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14
FakeRead(ForLet, _6); // bb1[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10
FakeRead(ForLet(None), _6); // bb1[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10
StorageLive(_7); // bb1[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12
_7 = const Const(Value(Scalar(0x01)): bool); // bb1[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12
switchInt(move _7) -> [Const(Value(Scalar(0x00)): bool): bb3, otherwise: bb2]; // bb1[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6

View File

@ -36,7 +36,7 @@ fn main() -> () {
}
bb1: {
FakeRead(ForLet, _1); // scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
FakeRead(ForLet(None), _1); // scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
AscribeUserType(_1, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/receiver-ptr-mutability.rs:14:14: 14:23
StorageLive(_2); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
StorageLive(_3); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
@ -63,7 +63,7 @@ fn main() -> () {
_7 = &_8; // scope 1 at $DIR/receiver-ptr-mutability.rs:18:35: 18:41
_6 = &_7; // scope 1 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
_5 = &(*_6); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
FakeRead(ForLet, _5); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
FakeRead(ForLet(None), _5); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
AscribeUserType(_5, o, UserTypeProjection { base: UserType(3), projs: [] }); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:18: 18:31
StorageDead(_6); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:41: 18:42
StorageLive(_10); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16

View File

@ -13,7 +13,7 @@
let mut _8: bool; // in scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21
bb0: {
- FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12
- FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12
+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12
_3 = discriminant(_1); // scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16
switchInt(move _3) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16

View File

@ -5,7 +5,7 @@ fn match_bool(_1: bool) -> usize {
let mut _0: usize; // return place in scope 0 at $DIR/simple-match.rs:5:27: 5:32
bb0: {
FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12
FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12
switchInt(_1) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13
}

View File

@ -5,7 +5,7 @@ fn match_bool(_1: bool) -> usize {
let mut _0: usize; // return place in scope 0 at $DIR/simple-match.rs:5:27: 5:32
bb0: {
FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12
FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12
switchInt(_1) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13
}

View File

@ -39,7 +39,7 @@ fn main() -> () {
bb0: {
StorageLive(_1); // scope 0 at $DIR/storage_ranges.rs:4:9: 4:10
_1 = const 0_i32; // scope 0 at $DIR/storage_ranges.rs:4:13: 4:14
FakeRead(ForLet, _1); // scope 0 at $DIR/storage_ranges.rs:4:9: 4:10
FakeRead(ForLet(None), _1); // scope 0 at $DIR/storage_ranges.rs:4:9: 4:10
StorageLive(_2); // scope 1 at $DIR/storage_ranges.rs:5:5: 7:6
StorageLive(_3); // scope 1 at $DIR/storage_ranges.rs:6:13: 6:14
StorageLive(_4); // scope 1 at $DIR/storage_ranges.rs:6:18: 6:25
@ -48,14 +48,14 @@ fn main() -> () {
_4 = Option::<i32>::Some(move _5); // scope 1 at $DIR/storage_ranges.rs:6:18: 6:25
StorageDead(_5); // scope 1 at $DIR/storage_ranges.rs:6:24: 6:25
_3 = &_4; // scope 1 at $DIR/storage_ranges.rs:6:17: 6:25
FakeRead(ForLet, _3); // scope 1 at $DIR/storage_ranges.rs:6:13: 6:14
FakeRead(ForLet(None), _3); // scope 1 at $DIR/storage_ranges.rs:6:13: 6:14
_2 = const (); // scope 1 at $DIR/storage_ranges.rs:5:5: 7:6
StorageDead(_4); // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6
StorageDead(_3); // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6
StorageDead(_2); // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6
StorageLive(_6); // scope 1 at $DIR/storage_ranges.rs:8:9: 8:10
_6 = const 1_i32; // scope 1 at $DIR/storage_ranges.rs:8:13: 8:14
FakeRead(ForLet, _6); // scope 1 at $DIR/storage_ranges.rs:8:9: 8:10
FakeRead(ForLet(None), _6); // scope 1 at $DIR/storage_ranges.rs:8:9: 8:10
_0 = const (); // scope 0 at $DIR/storage_ranges.rs:3:11: 9:2
StorageDead(_6); // scope 1 at $DIR/storage_ranges.rs:9:1: 9:2
StorageDead(_1); // scope 0 at $DIR/storage_ranges.rs:9:1: 9:2

View File

@ -48,7 +48,7 @@ fn move_out_by_subslice() -> () {
bb4: {
StorageDead(_2); // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27
FakeRead(ForLet, _1); // scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10
FakeRead(ForLet(None), _1); // scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10
StorageLive(_6); // scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17
_6 = move _1[0..2]; // scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17
_0 = const (); // scope 0 at $DIR/uniform_array_move_out.rs:10:27: 13:2

View File

@ -48,7 +48,7 @@ fn move_out_from_end() -> () {
bb4: {
StorageDead(_2); // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27
FakeRead(ForLet, _1); // scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10
FakeRead(ForLet(None), _1); // scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10
StorageLive(_6); // scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16
_6 = move _1[1 of 2]; // scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16
_0 = const (); // scope 0 at $DIR/uniform_array_move_out.rs:4:24: 7:2

View File

@ -212,7 +212,7 @@ fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statemen
check_rvalue(tcx, body, def_id, rval, span)
}
StatementKind::FakeRead(_, place) |
StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
// just an assignment
StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),

View File

@ -909,7 +909,8 @@ fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
// This particular form is documented in the GNU coding standards:
// https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion
let mut splits = full_version_line.rsplit(' ');
let unbracketed_part = full_version_line.split('[').next().unwrap();
let mut splits = unbracketed_part.trim_end().rsplit(' ');
let version_string = splits.next().unwrap();
let mut splits = version_string.split('.');

View File

@ -39,6 +39,9 @@ fn test_extract_gdb_version() {
7012000: "GNU gdb (GDB) 7.12",
7012000: "GNU gdb (GDB) 7.12.20161027-git",
7012050: "GNU gdb (GDB) 7.12.50.20161027-git",
9002000: "GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2",
10001000: "GNU gdb (GDB) 10.1 [GDB v10.1 for FreeBSD]",
}
}