Auto merge of #142689 - Urgau:rollup-4ho6835, r=Urgau
Rollup of 6 pull requests Successful merges: - rust-lang/rust#135656 (Add `-Z hint-mostly-unused` to tell rustc that most of a crate will go unused) - rust-lang/rust#138237 (Get rid of `EscapeDebugInner`.) - rust-lang/rust#141614 (lint direct use of rustc_type_ir ) - rust-lang/rust#142123 (Implement initial support for timing sections (`--json=timings`)) - rust-lang/rust#142377 (Try unremapping compiler sources) - rust-lang/rust#142674 (remove duplicate crash test) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
044514eb26
35 changed files with 783 additions and 192 deletions
|
|
@ -34,6 +34,7 @@ use crate::snippet::{
|
|||
Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString,
|
||||
};
|
||||
use crate::styled_buffer::StyledBuffer;
|
||||
use crate::timings::TimingRecord;
|
||||
use crate::translation::{Translate, to_fluent_args};
|
||||
use crate::{
|
||||
CodeSuggestion, DiagInner, DiagMessage, ErrCode, FluentBundle, LazyFallbackBundle, Level,
|
||||
|
|
@ -164,11 +165,16 @@ impl Margin {
|
|||
}
|
||||
}
|
||||
|
||||
pub enum TimingEvent {
|
||||
Start,
|
||||
End,
|
||||
}
|
||||
|
||||
const ANONYMIZED_LINE_NUM: &str = "LL";
|
||||
|
||||
pub type DynEmitter = dyn Emitter + DynSend;
|
||||
|
||||
/// Emitter trait for emitting errors.
|
||||
/// Emitter trait for emitting errors and other structured information.
|
||||
pub trait Emitter: Translate {
|
||||
/// Emit a structured diagnostic.
|
||||
fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry);
|
||||
|
|
@ -177,6 +183,10 @@ pub trait Emitter: Translate {
|
|||
/// Currently only supported for the JSON format.
|
||||
fn emit_artifact_notification(&mut self, _path: &Path, _artifact_type: &str) {}
|
||||
|
||||
/// Emit a timestamp with start/end of a timing section.
|
||||
/// Currently only supported for the JSON format.
|
||||
fn emit_timing_section(&mut self, _record: TimingRecord, _event: TimingEvent) {}
|
||||
|
||||
/// Emit a report about future breakage.
|
||||
/// Currently only supported for the JSON format.
|
||||
fn emit_future_breakage_report(&mut self, _diags: Vec<DiagInner>, _registry: &Registry) {}
|
||||
|
|
|
|||
|
|
@ -28,9 +28,10 @@ use termcolor::{ColorSpec, WriteColor};
|
|||
use crate::diagnostic::IsLint;
|
||||
use crate::emitter::{
|
||||
ColorConfig, Destination, Emitter, HumanEmitter, HumanReadableErrorType, OutputTheme,
|
||||
should_show_source_code,
|
||||
TimingEvent, should_show_source_code,
|
||||
};
|
||||
use crate::registry::Registry;
|
||||
use crate::timings::{TimingRecord, TimingSection};
|
||||
use crate::translation::{Translate, to_fluent_args};
|
||||
use crate::{
|
||||
CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, Subdiag, Suggestions,
|
||||
|
|
@ -104,6 +105,7 @@ impl JsonEmitter {
|
|||
enum EmitTyped<'a> {
|
||||
Diagnostic(Diagnostic),
|
||||
Artifact(ArtifactNotification<'a>),
|
||||
SectionTiming(SectionTimestamp<'a>),
|
||||
FutureIncompat(FutureIncompatReport<'a>),
|
||||
UnusedExtern(UnusedExterns<'a>),
|
||||
}
|
||||
|
|
@ -135,6 +137,21 @@ impl Emitter for JsonEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
fn emit_timing_section(&mut self, record: TimingRecord, event: TimingEvent) {
|
||||
let event = match event {
|
||||
TimingEvent::Start => "start",
|
||||
TimingEvent::End => "end",
|
||||
};
|
||||
let name = match record.section {
|
||||
TimingSection::Linking => "link",
|
||||
};
|
||||
let data = SectionTimestamp { name, event, timestamp: record.timestamp };
|
||||
let result = self.emit(EmitTyped::SectionTiming(data));
|
||||
if let Err(e) = result {
|
||||
panic!("failed to print timing section: {e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_future_breakage_report(&mut self, diags: Vec<crate::DiagInner>, registry: &Registry) {
|
||||
let data: Vec<FutureBreakageItem<'_>> = diags
|
||||
.into_iter()
|
||||
|
|
@ -263,6 +280,16 @@ struct ArtifactNotification<'a> {
|
|||
emit: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct SectionTimestamp<'a> {
|
||||
/// Name of the section
|
||||
name: &'a str,
|
||||
/// Start/end of the section
|
||||
event: &'a str,
|
||||
/// Opaque timestamp.
|
||||
timestamp: u128,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct FutureBreakageItem<'a> {
|
||||
// Always EmitTyped::Diagnostic, but we want to make sure it gets serialized
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![cfg_attr(not(bootstrap), allow(rustc::direct_use_of_rustc_type_ir))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(array_windows)]
|
||||
|
|
@ -74,7 +75,9 @@ pub use snippet::Style;
|
|||
pub use termcolor::{Color, ColorSpec, WriteColor};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::emitter::TimingEvent;
|
||||
use crate::registry::Registry;
|
||||
use crate::timings::TimingRecord;
|
||||
|
||||
pub mod annotate_snippet_emitter_writer;
|
||||
pub mod codes;
|
||||
|
|
@ -90,6 +93,7 @@ mod snippet;
|
|||
mod styled_buffer;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod timings;
|
||||
pub mod translation;
|
||||
|
||||
pub type PResult<'a, T> = Result<T, Diag<'a>>;
|
||||
|
|
@ -1156,6 +1160,14 @@ impl<'a> DiagCtxtHandle<'a> {
|
|||
self.inner.borrow_mut().emitter.emit_artifact_notification(path, artifact_type);
|
||||
}
|
||||
|
||||
pub fn emit_timing_section_start(&self, record: TimingRecord) {
|
||||
self.inner.borrow_mut().emitter.emit_timing_section(record, TimingEvent::Start);
|
||||
}
|
||||
|
||||
pub fn emit_timing_section_end(&self, record: TimingRecord) {
|
||||
self.inner.borrow_mut().emitter.emit_timing_section(record, TimingEvent::End);
|
||||
}
|
||||
|
||||
pub fn emit_future_breakage_report(&self) {
|
||||
let inner = &mut *self.inner.borrow_mut();
|
||||
let diags = std::mem::take(&mut inner.future_breakage_diagnostics);
|
||||
|
|
|
|||
80
compiler/rustc_errors/src/timings.rs
Normal file
80
compiler/rustc_errors/src/timings.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
use std::time::Instant;
|
||||
|
||||
use crate::DiagCtxtHandle;
|
||||
|
||||
/// A high-level section of the compilation process.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum TimingSection {
|
||||
/// Time spent linking.
|
||||
Linking,
|
||||
}
|
||||
|
||||
/// Section with attached timestamp
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct TimingRecord {
|
||||
pub section: TimingSection,
|
||||
/// Microseconds elapsed since some predetermined point in time (~start of the rustc process).
|
||||
pub timestamp: u128,
|
||||
}
|
||||
|
||||
impl TimingRecord {
|
||||
fn from_origin(origin: Instant, section: TimingSection) -> Self {
|
||||
Self { section, timestamp: Instant::now().duration_since(origin).as_micros() }
|
||||
}
|
||||
|
||||
pub fn section(&self) -> TimingSection {
|
||||
self.section
|
||||
}
|
||||
|
||||
pub fn timestamp(&self) -> u128 {
|
||||
self.timestamp
|
||||
}
|
||||
}
|
||||
|
||||
/// Manages emission of start/end section timings, enabled through `--json=timings`.
|
||||
pub struct TimingSectionHandler {
|
||||
/// Time when the compilation session started.
|
||||
/// If `None`, timing is disabled.
|
||||
origin: Option<Instant>,
|
||||
}
|
||||
|
||||
impl TimingSectionHandler {
|
||||
pub fn new(enabled: bool) -> Self {
|
||||
let origin = if enabled { Some(Instant::now()) } else { None };
|
||||
Self { origin }
|
||||
}
|
||||
|
||||
/// Returns a RAII guard that will immediately emit a start the provided section, and then emit
|
||||
/// its end when it is dropped.
|
||||
pub fn start_section<'a>(
|
||||
&self,
|
||||
diag_ctxt: DiagCtxtHandle<'a>,
|
||||
section: TimingSection,
|
||||
) -> TimingSectionGuard<'a> {
|
||||
TimingSectionGuard::create(diag_ctxt, section, self.origin)
|
||||
}
|
||||
}
|
||||
|
||||
/// RAII wrapper for starting and ending section timings.
|
||||
pub struct TimingSectionGuard<'a> {
|
||||
dcx: DiagCtxtHandle<'a>,
|
||||
section: TimingSection,
|
||||
origin: Option<Instant>,
|
||||
}
|
||||
|
||||
impl<'a> TimingSectionGuard<'a> {
|
||||
fn create(dcx: DiagCtxtHandle<'a>, section: TimingSection, origin: Option<Instant>) -> Self {
|
||||
if let Some(origin) = origin {
|
||||
dcx.emit_timing_section_start(TimingRecord::from_origin(origin, section));
|
||||
}
|
||||
Self { dcx, section, origin }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for TimingSectionGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(origin) = self.origin {
|
||||
self.dcx.emit_timing_section_end(TimingRecord::from_origin(origin, self.section));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![cfg_attr(not(bootstrap), allow(rustc::direct_use_of_rustc_type_ir))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use std::sync::Arc;
|
|||
use rustc_codegen_ssa::CodegenResults;
|
||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_errors::timings::TimingSection;
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_metadata::EncodedMetadata;
|
||||
use rustc_middle::dep_graph::DepGraph;
|
||||
|
|
@ -88,6 +89,7 @@ impl Linker {
|
|||
}
|
||||
|
||||
let _timer = sess.prof.verbose_generic_activity("link_crate");
|
||||
let _timing = sess.timings.start_section(sess.dcx(), TimingSection::Linking);
|
||||
codegen_backend.link(sess, codegen_results, self.metadata, &self.output_filenames)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -802,6 +802,7 @@ fn test_unstable_options_tracking_hash() {
|
|||
tracked!(force_unstable_if_unmarked, true);
|
||||
tracked!(function_return, FunctionReturn::ThunkExtern);
|
||||
tracked!(function_sections, Some(false));
|
||||
tracked!(hint_mostly_unused, true);
|
||||
tracked!(human_readable_cgu_names, true);
|
||||
tracked!(incremental_ignore_spans, true);
|
||||
tracked!(inline_mir, Some(true));
|
||||
|
|
|
|||
|
|
@ -812,6 +812,9 @@ lint_tykind = usage of `ty::TyKind`
|
|||
lint_tykind_kind = usage of `ty::TyKind::<kind>`
|
||||
.suggestion = try using `ty::<kind>` directly
|
||||
|
||||
lint_type_ir_direct_use = do not use `rustc_type_ir` unless you are implementing type system internals
|
||||
.note = use `rustc_middle::ty` instead
|
||||
|
||||
lint_type_ir_inherent_usage = do not use `rustc_type_ir::inherent` unless you're inside of the trait solver
|
||||
.note = the method or struct you're looking for is likely defined somewhere else downstream in the compiler
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ use {rustc_ast as ast, rustc_hir as hir};
|
|||
use crate::lints::{
|
||||
BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand,
|
||||
NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag,
|
||||
SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrInherentUsage,
|
||||
TypeIrTraitUsage, UntranslatableDiag,
|
||||
SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrDirectUse,
|
||||
TypeIrInherentUsage, TypeIrTraitUsage, UntranslatableDiag,
|
||||
};
|
||||
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
||||
|
||||
|
|
@ -301,8 +301,18 @@ declare_tool_lint! {
|
|||
"usage `rustc_type_ir`-specific abstraction traits outside of trait system",
|
||||
report_in_external_macro: true
|
||||
}
|
||||
declare_tool_lint! {
|
||||
/// The `direct_use_of_rustc_type_ir` lint detects usage of `rustc_type_ir`.
|
||||
///
|
||||
/// This module should only be used within the trait solver and some desirable
|
||||
/// crates like rustc_middle.
|
||||
pub rustc::DIRECT_USE_OF_RUSTC_TYPE_IR,
|
||||
Allow,
|
||||
"usage `rustc_type_ir` abstraction outside of trait system",
|
||||
report_in_external_macro: true
|
||||
}
|
||||
|
||||
declare_lint_pass!(TypeIr => [NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_TRAITS]);
|
||||
declare_lint_pass!(TypeIr => [DIRECT_USE_OF_RUSTC_TYPE_IR, NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_TRAITS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for TypeIr {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
|
||||
|
|
@ -372,6 +382,21 @@ impl<'tcx> LateLintPass<'tcx> for TypeIr {
|
|||
NonGlobImportTypeIrInherent { suggestion: lo.eq_ctxt(hi).then(|| lo.to(hi)), snippet },
|
||||
);
|
||||
}
|
||||
|
||||
fn check_path(
|
||||
&mut self,
|
||||
cx: &LateContext<'tcx>,
|
||||
path: &rustc_hir::Path<'tcx>,
|
||||
_: rustc_hir::HirId,
|
||||
) {
|
||||
if let Some(seg) = path.segments.iter().find(|seg| {
|
||||
seg.res
|
||||
.opt_def_id()
|
||||
.is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::type_ir, def_id))
|
||||
}) {
|
||||
cx.emit_span_lint(DIRECT_USE_OF_RUSTC_TYPE_IR, seg.ident.span, TypeIrDirectUse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_tool_lint! {
|
||||
|
|
|
|||
|
|
@ -668,6 +668,7 @@ fn register_internals(store: &mut LintStore) {
|
|||
LintId::of(USAGE_OF_TYPE_IR_TRAITS),
|
||||
LintId::of(BAD_OPT_ACCESS),
|
||||
LintId::of(SPAN_USE_EQ_CTXT),
|
||||
LintId::of(DIRECT_USE_OF_RUSTC_TYPE_IR),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -969,6 +969,11 @@ pub(crate) struct TypeIrInherentUsage;
|
|||
#[note]
|
||||
pub(crate) struct TypeIrTraitUsage;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_type_ir_direct_use)]
|
||||
#[note]
|
||||
pub(crate) struct TypeIrDirectUse;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_non_glob_import_type_ir_inherent)]
|
||||
pub(crate) struct NonGlobImportTypeIrInherent {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Decoding metadata from a single crate's metadata
|
||||
|
||||
use std::iter::TrustedLen;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use std::{io, iter, mem};
|
||||
|
||||
|
|
@ -1610,10 +1610,14 @@ impl<'a> CrateMetadataRef<'a> {
|
|||
/// Proc macro crates don't currently export spans, so this function does not have
|
||||
/// to work for them.
|
||||
fn imported_source_file(self, source_file_index: u32, sess: &Session) -> ImportedSourceFile {
|
||||
fn filter<'a>(sess: &Session, path: Option<&'a Path>) -> Option<&'a Path> {
|
||||
fn filter<'a>(
|
||||
sess: &Session,
|
||||
real_source_base_dir: &Option<PathBuf>,
|
||||
path: Option<&'a Path>,
|
||||
) -> Option<&'a Path> {
|
||||
path.filter(|_| {
|
||||
// Only spend time on further checks if we have what to translate *to*.
|
||||
sess.opts.real_rust_source_base_dir.is_some()
|
||||
real_source_base_dir.is_some()
|
||||
// Some tests need the translation to be always skipped.
|
||||
&& sess.opts.unstable_opts.translate_remapped_path_to_local_path
|
||||
})
|
||||
|
|
@ -1625,57 +1629,92 @@ impl<'a> CrateMetadataRef<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
let try_to_translate_virtual_to_real = |name: &mut rustc_span::FileName| {
|
||||
// Translate the virtual `/rustc/$hash` prefix back to a real directory
|
||||
// that should hold actual sources, where possible.
|
||||
//
|
||||
// NOTE: if you update this, you might need to also update bootstrap's code for generating
|
||||
// the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`.
|
||||
let virtual_rust_source_base_dir = [
|
||||
filter(sess, option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(Path::new)),
|
||||
filter(sess, sess.opts.unstable_opts.simulate_remapped_rust_src_base.as_deref()),
|
||||
];
|
||||
let try_to_translate_virtual_to_real =
|
||||
|virtual_source_base_dir: Option<&str>,
|
||||
real_source_base_dir: &Option<PathBuf>,
|
||||
name: &mut rustc_span::FileName| {
|
||||
let virtual_source_base_dir = [
|
||||
filter(sess, real_source_base_dir, virtual_source_base_dir.map(Path::new)),
|
||||
filter(
|
||||
sess,
|
||||
real_source_base_dir,
|
||||
sess.opts.unstable_opts.simulate_remapped_rust_src_base.as_deref(),
|
||||
),
|
||||
];
|
||||
|
||||
debug!(
|
||||
"try_to_translate_virtual_to_real(name={:?}): \
|
||||
virtual_rust_source_base_dir={:?}, real_rust_source_base_dir={:?}",
|
||||
name, virtual_rust_source_base_dir, sess.opts.real_rust_source_base_dir,
|
||||
);
|
||||
debug!(
|
||||
"try_to_translate_virtual_to_real(name={:?}): \
|
||||
virtual_source_base_dir={:?}, real_source_base_dir={:?}",
|
||||
name, virtual_source_base_dir, real_source_base_dir,
|
||||
);
|
||||
|
||||
for virtual_dir in virtual_rust_source_base_dir.iter().flatten() {
|
||||
if let Some(real_dir) = &sess.opts.real_rust_source_base_dir
|
||||
&& let rustc_span::FileName::Real(old_name) = name
|
||||
&& let rustc_span::RealFileName::Remapped { local_path: _, virtual_name } =
|
||||
old_name
|
||||
&& let Ok(rest) = virtual_name.strip_prefix(virtual_dir)
|
||||
{
|
||||
let new_path = real_dir.join(rest);
|
||||
for virtual_dir in virtual_source_base_dir.iter().flatten() {
|
||||
if let Some(real_dir) = &real_source_base_dir
|
||||
&& let rustc_span::FileName::Real(old_name) = name
|
||||
&& let rustc_span::RealFileName::Remapped { local_path: _, virtual_name } =
|
||||
old_name
|
||||
&& let Ok(rest) = virtual_name.strip_prefix(virtual_dir)
|
||||
{
|
||||
let new_path = real_dir.join(rest);
|
||||
|
||||
debug!(
|
||||
"try_to_translate_virtual_to_real: `{}` -> `{}`",
|
||||
virtual_name.display(),
|
||||
new_path.display(),
|
||||
);
|
||||
debug!(
|
||||
"try_to_translate_virtual_to_real: `{}` -> `{}`",
|
||||
virtual_name.display(),
|
||||
new_path.display(),
|
||||
);
|
||||
|
||||
// Check if the translated real path is affected by any user-requested
|
||||
// remaps via --remap-path-prefix. Apply them if so.
|
||||
// Note that this is a special case for imported rust-src paths specified by
|
||||
// https://rust-lang.github.io/rfcs/3127-trim-paths.html#handling-sysroot-paths.
|
||||
// Other imported paths are not currently remapped (see #66251).
|
||||
let (user_remapped, applied) =
|
||||
sess.source_map().path_mapping().map_prefix(&new_path);
|
||||
let new_name = if applied {
|
||||
rustc_span::RealFileName::Remapped {
|
||||
local_path: Some(new_path.clone()),
|
||||
virtual_name: user_remapped.to_path_buf(),
|
||||
}
|
||||
} else {
|
||||
rustc_span::RealFileName::LocalPath(new_path)
|
||||
};
|
||||
*old_name = new_name;
|
||||
// Check if the translated real path is affected by any user-requested
|
||||
// remaps via --remap-path-prefix. Apply them if so.
|
||||
// Note that this is a special case for imported rust-src paths specified by
|
||||
// https://rust-lang.github.io/rfcs/3127-trim-paths.html#handling-sysroot-paths.
|
||||
// Other imported paths are not currently remapped (see #66251).
|
||||
let (user_remapped, applied) =
|
||||
sess.source_map().path_mapping().map_prefix(&new_path);
|
||||
let new_name = if applied {
|
||||
rustc_span::RealFileName::Remapped {
|
||||
local_path: Some(new_path.clone()),
|
||||
virtual_name: user_remapped.to_path_buf(),
|
||||
}
|
||||
} else {
|
||||
rustc_span::RealFileName::LocalPath(new_path)
|
||||
};
|
||||
*old_name = new_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
let try_to_translate_real_to_virtual =
|
||||
|virtual_source_base_dir: Option<&str>,
|
||||
real_source_base_dir: &Option<PathBuf>,
|
||||
subdir: &str,
|
||||
name: &mut rustc_span::FileName| {
|
||||
if let Some(virtual_dir) = &sess.opts.unstable_opts.simulate_remapped_rust_src_base
|
||||
&& let Some(real_dir) = real_source_base_dir
|
||||
&& let rustc_span::FileName::Real(old_name) = name
|
||||
{
|
||||
let relative_path = match old_name {
|
||||
rustc_span::RealFileName::LocalPath(local) => {
|
||||
local.strip_prefix(real_dir).ok()
|
||||
}
|
||||
rustc_span::RealFileName::Remapped { virtual_name, .. } => {
|
||||
virtual_source_base_dir
|
||||
.and_then(|virtual_dir| virtual_name.strip_prefix(virtual_dir).ok())
|
||||
}
|
||||
};
|
||||
debug!(
|
||||
?relative_path,
|
||||
?virtual_dir,
|
||||
?subdir,
|
||||
"simulate_remapped_rust_src_base"
|
||||
);
|
||||
if let Some(rest) = relative_path.and_then(|p| p.strip_prefix(subdir).ok()) {
|
||||
*old_name = rustc_span::RealFileName::Remapped {
|
||||
local_path: None,
|
||||
virtual_name: virtual_dir.join(subdir).join(rest),
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut import_info = self.cdata.source_map_import_info.lock();
|
||||
for _ in import_info.len()..=(source_file_index as usize) {
|
||||
|
|
@ -1713,36 +1752,45 @@ impl<'a> CrateMetadataRef<'a> {
|
|||
// This is useful for testing so that tests about the effects of
|
||||
// `try_to_translate_virtual_to_real` don't have to worry about how the
|
||||
// compiler is bootstrapped.
|
||||
if let Some(virtual_dir) = &sess.opts.unstable_opts.simulate_remapped_rust_src_base
|
||||
&& let Some(real_dir) = &sess.opts.real_rust_source_base_dir
|
||||
&& let rustc_span::FileName::Real(ref mut old_name) = name
|
||||
{
|
||||
let relative_path = match old_name {
|
||||
rustc_span::RealFileName::LocalPath(local) => {
|
||||
local.strip_prefix(real_dir).ok()
|
||||
}
|
||||
rustc_span::RealFileName::Remapped { virtual_name, .. } => {
|
||||
option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR")
|
||||
.and_then(|virtual_dir| virtual_name.strip_prefix(virtual_dir).ok())
|
||||
}
|
||||
};
|
||||
debug!(?relative_path, ?virtual_dir, "simulate_remapped_rust_src_base");
|
||||
for subdir in ["library", "compiler"] {
|
||||
if let Some(rest) = relative_path.and_then(|p| p.strip_prefix(subdir).ok())
|
||||
{
|
||||
*old_name = rustc_span::RealFileName::Remapped {
|
||||
local_path: None, // FIXME: maybe we should preserve this?
|
||||
virtual_name: virtual_dir.join(subdir).join(rest),
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
try_to_translate_real_to_virtual(
|
||||
option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR"),
|
||||
&sess.opts.real_rust_source_base_dir,
|
||||
"library",
|
||||
&mut name,
|
||||
);
|
||||
|
||||
// If this file is under $sysroot/lib/rustlib/rustc-src/
|
||||
// and the user wish to simulate remapping with -Z simulate-remapped-rust-src-base,
|
||||
// then we change `name` to a similar state as if the rust was bootstrapped
|
||||
// with `remap-debuginfo = true`.
|
||||
try_to_translate_real_to_virtual(
|
||||
option_env!("CFG_VIRTUAL_RUSTC_DEV_SOURCE_BASE_DIR"),
|
||||
&sess.opts.real_rustc_dev_source_base_dir,
|
||||
"compiler",
|
||||
&mut name,
|
||||
);
|
||||
|
||||
// If this file's path has been remapped to `/rustc/$hash`,
|
||||
// we might be able to reverse that (also see comments above,
|
||||
// on `try_to_translate_virtual_to_real`).
|
||||
try_to_translate_virtual_to_real(&mut name);
|
||||
// we might be able to reverse that.
|
||||
//
|
||||
// NOTE: if you update this, you might need to also update bootstrap's code for generating
|
||||
// the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`.
|
||||
try_to_translate_virtual_to_real(
|
||||
option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR"),
|
||||
&sess.opts.real_rust_source_base_dir,
|
||||
&mut name,
|
||||
);
|
||||
|
||||
// If this file's path has been remapped to `/rustc-dev/$hash`,
|
||||
// we might be able to reverse that.
|
||||
//
|
||||
// NOTE: if you update this, you might need to also update bootstrap's code for generating
|
||||
// the `rustc-dev` component in `Src::run` in `src/bootstrap/dist.rs`.
|
||||
try_to_translate_virtual_to_real(
|
||||
option_env!("CFG_VIRTUAL_RUSTC_DEV_SOURCE_BASE_DIR"),
|
||||
&sess.opts.real_rustc_dev_source_base_dir,
|
||||
&mut name,
|
||||
);
|
||||
|
||||
let local_version = sess.source_map().new_imported_source_file(
|
||||
name,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![cfg_attr(not(bootstrap), allow(rustc::direct_use_of_rustc_type_ir))]
|
||||
#![cfg_attr(not(bootstrap), feature(sized_hierarchy))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
|
|
|
|||
|
|
@ -50,6 +50,13 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
|||
_ => {}
|
||||
}
|
||||
|
||||
// If the crate is likely to be mostly unused, use cross-crate inlining to defer codegen until
|
||||
// the function is referenced, in order to skip codegen for unused functions. This is
|
||||
// intentionally after the check for `inline(never)`, so that `inline(never)` wins.
|
||||
if tcx.sess.opts.unstable_opts.hint_mostly_unused {
|
||||
return true;
|
||||
}
|
||||
|
||||
let sig = tcx.fn_sig(def_id).instantiate_identity();
|
||||
for ty in sig.inputs().skip_binder().iter().chain(std::iter::once(&sig.output().skip_binder()))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
// tidy-alphabetical-start
|
||||
#![allow(rustc::usage_of_type_ir_inherent)]
|
||||
#![allow(rustc::usage_of_type_ir_traits)]
|
||||
#![cfg_attr(not(bootstrap), allow(rustc::direct_use_of_rustc_type_ir))]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
pub mod canonicalizer;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
//! * Compiler internal types like `Ty` and `TyCtxt`
|
||||
|
||||
use rustc_hir::diagnostic_items::DiagnosticItems;
|
||||
use rustc_hir::{Attribute, OwnerId};
|
||||
use rustc_hir::{Attribute, CRATE_OWNER_ID, OwnerId};
|
||||
use rustc_middle::query::{LocalCrate, Providers};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::def_id::{DefId, LOCAL_CRATE};
|
||||
|
|
@ -67,7 +67,7 @@ fn diagnostic_items(tcx: TyCtxt<'_>, _: LocalCrate) -> DiagnosticItems {
|
|||
|
||||
// Collect diagnostic items in this crate.
|
||||
let crate_items = tcx.hir_crate_items(());
|
||||
for id in crate_items.owners() {
|
||||
for id in crate_items.owners().chain(std::iter::once(CRATE_OWNER_ID)) {
|
||||
observe_item(tcx, &mut diagnostic_items, id);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1364,8 +1364,10 @@ impl Default for Options {
|
|||
cli_forced_local_thinlto_off: false,
|
||||
remap_path_prefix: Vec::new(),
|
||||
real_rust_source_base_dir: None,
|
||||
real_rustc_dev_source_base_dir: None,
|
||||
edition: DEFAULT_EDITION,
|
||||
json_artifact_notifications: false,
|
||||
json_timings: false,
|
||||
json_unused_externs: JsonUnusedExterns::No,
|
||||
json_future_incompat: false,
|
||||
pretty: None,
|
||||
|
|
@ -1880,6 +1882,9 @@ pub struct JsonConfig {
|
|||
pub json_rendered: HumanReadableErrorType,
|
||||
pub json_color: ColorConfig,
|
||||
json_artifact_notifications: bool,
|
||||
/// Output start and end timestamps of several high-level compilation sections
|
||||
/// (frontend, backend, linker).
|
||||
json_timings: bool,
|
||||
pub json_unused_externs: JsonUnusedExterns,
|
||||
json_future_incompat: bool,
|
||||
}
|
||||
|
|
@ -1921,6 +1926,7 @@ pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Json
|
|||
let mut json_artifact_notifications = false;
|
||||
let mut json_unused_externs = JsonUnusedExterns::No;
|
||||
let mut json_future_incompat = false;
|
||||
let mut json_timings = 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
|
||||
|
|
@ -1937,6 +1943,7 @@ pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Json
|
|||
}
|
||||
"diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
|
||||
"artifacts" => json_artifact_notifications = true,
|
||||
"timings" => json_timings = true,
|
||||
"unused-externs" => json_unused_externs = JsonUnusedExterns::Loud,
|
||||
"unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent,
|
||||
"future-incompat" => json_future_incompat = true,
|
||||
|
|
@ -1949,6 +1956,7 @@ pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Json
|
|||
json_rendered,
|
||||
json_color,
|
||||
json_artifact_notifications,
|
||||
json_timings,
|
||||
json_unused_externs,
|
||||
json_future_incompat,
|
||||
}
|
||||
|
|
@ -2476,6 +2484,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
|
|||
json_rendered,
|
||||
json_color,
|
||||
json_artifact_notifications,
|
||||
json_timings,
|
||||
json_unused_externs,
|
||||
json_future_incompat,
|
||||
} = parse_json(early_dcx, matches);
|
||||
|
|
@ -2497,6 +2506,10 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
|
|||
let mut unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers);
|
||||
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
|
||||
|
||||
if !unstable_opts.unstable_options && json_timings {
|
||||
early_dcx.early_fatal("--json=timings is unstable and requires using `-Zunstable-options`");
|
||||
}
|
||||
|
||||
check_error_format_stability(early_dcx, &unstable_opts, error_format);
|
||||
|
||||
let output_types = parse_output_types(early_dcx, &unstable_opts, matches);
|
||||
|
|
@ -2701,9 +2714,8 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
|
|||
|
||||
let sysroot = filesearch::materialize_sysroot(sysroot_opt);
|
||||
|
||||
let real_rust_source_base_dir = {
|
||||
// This is the location used by the `rust-src` `rustup` component.
|
||||
let mut candidate = sysroot.join("lib/rustlib/src/rust");
|
||||
let real_source_base_dir = |suffix: &str, confirm: &str| {
|
||||
let mut candidate = sysroot.join(suffix);
|
||||
if let Ok(metadata) = candidate.symlink_metadata() {
|
||||
// Replace the symlink bootstrap creates, with its destination.
|
||||
// We could try to use `fs::canonicalize` instead, but that might
|
||||
|
|
@ -2716,9 +2728,17 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
|
|||
}
|
||||
|
||||
// Only use this directory if it has a file we can expect to always find.
|
||||
candidate.join("library/std/src/lib.rs").is_file().then_some(candidate)
|
||||
candidate.join(confirm).is_file().then_some(candidate)
|
||||
};
|
||||
|
||||
let real_rust_source_base_dir =
|
||||
// This is the location used by the `rust-src` `rustup` component.
|
||||
real_source_base_dir("lib/rustlib/src/rust", "library/std/src/lib.rs");
|
||||
|
||||
let real_rustc_dev_source_base_dir =
|
||||
// This is the location used by the `rustc-dev` `rustup` component.
|
||||
real_source_base_dir("lib/rustlib/rustc-src/rust", "compiler/rustc/src/main.rs");
|
||||
|
||||
let mut search_paths = vec![];
|
||||
for s in &matches.opt_strs("L") {
|
||||
search_paths.push(SearchPath::from_cli_opt(
|
||||
|
|
@ -2772,8 +2792,10 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
|
|||
cli_forced_local_thinlto_off: disable_local_thinlto,
|
||||
remap_path_prefix,
|
||||
real_rust_source_base_dir,
|
||||
real_rustc_dev_source_base_dir,
|
||||
edition,
|
||||
json_artifact_notifications,
|
||||
json_timings,
|
||||
json_unused_externs,
|
||||
json_future_incompat,
|
||||
pretty,
|
||||
|
|
|
|||
|
|
@ -395,21 +395,35 @@ top_level_options!(
|
|||
|
||||
/// Remap source path prefixes in all output (messages, object files, debug, etc.).
|
||||
remap_path_prefix: Vec<(PathBuf, PathBuf)> [TRACKED_NO_CRATE_HASH],
|
||||
/// Base directory containing the `src/` for the Rust standard library, and
|
||||
/// potentially `rustc` as well, if we can find it. Right now it's always
|
||||
/// `$sysroot/lib/rustlib/src/rust` (i.e. the `rustup` `rust-src` component).
|
||||
|
||||
/// Base directory containing the `library/` directory for the Rust standard library.
|
||||
/// Right now it's always `$sysroot/lib/rustlib/src/rust`
|
||||
/// (i.e. the `rustup` `rust-src` component).
|
||||
///
|
||||
/// This directory is what the virtual `/rustc/$hash` is translated back to,
|
||||
/// if Rust was built with path remapping to `/rustc/$hash` enabled
|
||||
/// (the `rust.remap-debuginfo` option in `bootstrap.toml`).
|
||||
real_rust_source_base_dir: Option<PathBuf> [TRACKED_NO_CRATE_HASH],
|
||||
|
||||
/// Base directory containing the `compiler/` directory for the rustc sources.
|
||||
/// Right now it's always `$sysroot/lib/rustlib/rustc-src/rust`
|
||||
/// (i.e. the `rustup` `rustc-dev` component).
|
||||
///
|
||||
/// This directory is what the virtual `/rustc-dev/$hash` is translated back to,
|
||||
/// if Rust was built with path remapping to `/rustc/$hash` enabled
|
||||
/// (the `rust.remap-debuginfo` option in `bootstrap.toml`).
|
||||
real_rustc_dev_source_base_dir: Option<PathBuf> [TRACKED_NO_CRATE_HASH],
|
||||
|
||||
edition: Edition [TRACKED],
|
||||
|
||||
/// `true` if we're emitting JSON blobs about each artifact produced
|
||||
/// by the compiler.
|
||||
json_artifact_notifications: bool [TRACKED],
|
||||
|
||||
/// `true` if we're emitting JSON timings with the start and end of
|
||||
/// high-level compilation sections
|
||||
json_timings: bool [UNTRACKED],
|
||||
|
||||
/// `true` if we're emitting a JSON blob containing the unused externs
|
||||
json_unused_externs: JsonUnusedExterns [UNTRACKED],
|
||||
|
||||
|
|
@ -2235,6 +2249,8 @@ options! {
|
|||
environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"),
|
||||
has_thread_local: Option<bool> = (None, parse_opt_bool, [TRACKED],
|
||||
"explicitly enable the `cfg(target_thread_local)` directive"),
|
||||
hint_mostly_unused: bool = (false, parse_bool, [TRACKED],
|
||||
"hint that most of this crate will go unused, to minimize work for uncalled functions"),
|
||||
human_readable_cgu_names: bool = (false, parse_bool, [TRACKED],
|
||||
"generate human-readable, predictable names for codegen units (default: no)"),
|
||||
identify_regions: bool = (false, parse_bool, [UNTRACKED],
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use rustc_errors::emitter::{
|
|||
DynEmitter, HumanEmitter, HumanReadableErrorType, OutputTheme, stderr_destination,
|
||||
};
|
||||
use rustc_errors::json::JsonEmitter;
|
||||
use rustc_errors::timings::TimingSectionHandler;
|
||||
use rustc_errors::{
|
||||
Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort,
|
||||
FluentBundle, LazyFallbackBundle, TerminalUrl, fallback_fluent_bundle,
|
||||
|
|
@ -156,6 +157,9 @@ pub struct Session {
|
|||
/// Used by `-Z self-profile`.
|
||||
pub prof: SelfProfilerRef,
|
||||
|
||||
/// Used to emit section timings events (enabled by `--json=timings`).
|
||||
pub timings: TimingSectionHandler,
|
||||
|
||||
/// Data about code being compiled, gathered during compilation.
|
||||
pub code_stats: CodeStats,
|
||||
|
||||
|
|
@ -1126,6 +1130,8 @@ pub fn build_session(
|
|||
.as_ref()
|
||||
.map(|_| rng().next_u32().to_base_fixed_len(CASE_INSENSITIVE).to_string());
|
||||
|
||||
let timings = TimingSectionHandler::new(sopts.json_timings);
|
||||
|
||||
let sess = Session {
|
||||
target,
|
||||
host,
|
||||
|
|
@ -1136,6 +1142,7 @@ pub fn build_session(
|
|||
io,
|
||||
incr_comp_session: RwLock::new(IncrCompSession::NotInitialized),
|
||||
prof,
|
||||
timings,
|
||||
code_stats: Default::default(),
|
||||
lint_store: None,
|
||||
driver_lint_caps,
|
||||
|
|
|
|||
|
|
@ -2183,6 +2183,7 @@ symbols! {
|
|||
type_changing_struct_update,
|
||||
type_const,
|
||||
type_id,
|
||||
type_ir,
|
||||
type_ir_infer_ctxt_like,
|
||||
type_ir_inherent,
|
||||
type_ir_interner,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir")]
|
||||
// tidy-alphabetical-start
|
||||
#![allow(rustc::usage_of_ty_tykind)]
|
||||
#![allow(rustc::usage_of_type_ir_inherent)]
|
||||
|
|
@ -7,6 +8,7 @@
|
|||
feature(associated_type_defaults, never_type, rustc_attrs, negative_impls)
|
||||
)]
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), allow(rustc::direct_use_of_rustc_type_ir))]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
extern crate self as rustc_type_ir;
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@
|
|||
|
||||
#![stable(feature = "core_ascii", since = "1.26.0")]
|
||||
|
||||
use crate::escape::{AlwaysEscaped, EscapeIterInner};
|
||||
use crate::fmt;
|
||||
use crate::iter::FusedIterator;
|
||||
use crate::num::NonZero;
|
||||
use crate::{escape, fmt};
|
||||
|
||||
mod ascii_char;
|
||||
#[unstable(feature = "ascii_char", issue = "110998")]
|
||||
|
|
@ -24,7 +25,7 @@ pub use ascii_char::AsciiChar as Char;
|
|||
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[derive(Clone)]
|
||||
pub struct EscapeDefault(escape::EscapeIterInner<4>);
|
||||
pub struct EscapeDefault(EscapeIterInner<4, AlwaysEscaped>);
|
||||
|
||||
/// Returns an iterator that produces an escaped version of a `u8`.
|
||||
///
|
||||
|
|
@ -96,17 +97,12 @@ pub fn escape_default(c: u8) -> EscapeDefault {
|
|||
impl EscapeDefault {
|
||||
#[inline]
|
||||
pub(crate) const fn new(c: u8) -> Self {
|
||||
Self(escape::EscapeIterInner::ascii(c))
|
||||
Self(EscapeIterInner::ascii(c))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn empty() -> Self {
|
||||
Self(escape::EscapeIterInner::empty())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn as_str(&self) -> &str {
|
||||
self.0.as_str()
|
||||
Self(EscapeIterInner::empty())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -168,7 +164,7 @@ impl FusedIterator for EscapeDefault {}
|
|||
#[stable(feature = "ascii_escape_display", since = "1.39.0")]
|
||||
impl fmt::Display for EscapeDefault {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.0.as_str())
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ pub use self::methods::{encode_utf8_raw, encode_utf8_raw_unchecked}; // perma-un
|
|||
use crate::ascii;
|
||||
pub(crate) use self::methods::EscapeDebugExtArgs;
|
||||
use crate::error::Error;
|
||||
use crate::escape;
|
||||
use crate::escape::{AlwaysEscaped, EscapeIterInner, MaybeEscaped};
|
||||
use crate::fmt::{self, Write};
|
||||
use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce};
|
||||
use crate::num::NonZero;
|
||||
|
|
@ -161,12 +161,12 @@ pub const fn from_digit(num: u32, radix: u32) -> Option<char> {
|
|||
/// [`escape_unicode`]: char::escape_unicode
|
||||
#[derive(Clone, Debug)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct EscapeUnicode(escape::EscapeIterInner<10>);
|
||||
pub struct EscapeUnicode(EscapeIterInner<10, AlwaysEscaped>);
|
||||
|
||||
impl EscapeUnicode {
|
||||
#[inline]
|
||||
const fn new(c: char) -> Self {
|
||||
Self(escape::EscapeIterInner::unicode(c))
|
||||
Self(EscapeIterInner::unicode(c))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,7 +215,7 @@ impl FusedIterator for EscapeUnicode {}
|
|||
#[stable(feature = "char_struct_display", since = "1.16.0")]
|
||||
impl fmt::Display for EscapeUnicode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.0.as_str())
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -227,22 +227,22 @@ impl fmt::Display for EscapeUnicode {
|
|||
/// [`escape_default`]: char::escape_default
|
||||
#[derive(Clone, Debug)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct EscapeDefault(escape::EscapeIterInner<10>);
|
||||
pub struct EscapeDefault(EscapeIterInner<10, AlwaysEscaped>);
|
||||
|
||||
impl EscapeDefault {
|
||||
#[inline]
|
||||
const fn printable(c: ascii::Char) -> Self {
|
||||
Self(escape::EscapeIterInner::ascii(c.to_u8()))
|
||||
Self(EscapeIterInner::ascii(c.to_u8()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn backslash(c: ascii::Char) -> Self {
|
||||
Self(escape::EscapeIterInner::backslash(c))
|
||||
Self(EscapeIterInner::backslash(c))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn unicode(c: char) -> Self {
|
||||
Self(escape::EscapeIterInner::unicode(c))
|
||||
Self(EscapeIterInner::unicode(c))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -290,8 +290,9 @@ impl FusedIterator for EscapeDefault {}
|
|||
|
||||
#[stable(feature = "char_struct_display", since = "1.16.0")]
|
||||
impl fmt::Display for EscapeDefault {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.0.as_str())
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -303,37 +304,22 @@ impl fmt::Display for EscapeDefault {
|
|||
/// [`escape_debug`]: char::escape_debug
|
||||
#[stable(feature = "char_escape_debug", since = "1.20.0")]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EscapeDebug(EscapeDebugInner);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
// Note: It’s possible to manually encode the EscapeDebugInner inside of
|
||||
// EscapeIterInner (e.g. with alive=254..255 indicating that data[0..4] holds
|
||||
// a char) which would likely result in a more optimised code. For now we use
|
||||
// the option easier to implement.
|
||||
enum EscapeDebugInner {
|
||||
Bytes(escape::EscapeIterInner<10>),
|
||||
Char(char),
|
||||
}
|
||||
pub struct EscapeDebug(EscapeIterInner<10, MaybeEscaped>);
|
||||
|
||||
impl EscapeDebug {
|
||||
#[inline]
|
||||
const fn printable(chr: char) -> Self {
|
||||
Self(EscapeDebugInner::Char(chr))
|
||||
Self(EscapeIterInner::printable(chr))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn backslash(c: ascii::Char) -> Self {
|
||||
Self(EscapeDebugInner::Bytes(escape::EscapeIterInner::backslash(c)))
|
||||
Self(EscapeIterInner::backslash(c))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn unicode(c: char) -> Self {
|
||||
Self(EscapeDebugInner::Bytes(escape::EscapeIterInner::unicode(c)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clear(&mut self) {
|
||||
self.0 = EscapeDebugInner::Bytes(escape::EscapeIterInner::empty());
|
||||
Self(EscapeIterInner::unicode(c))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -343,13 +329,7 @@ impl Iterator for EscapeDebug {
|
|||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<char> {
|
||||
match self.0 {
|
||||
EscapeDebugInner::Bytes(ref mut bytes) => bytes.next().map(char::from),
|
||||
EscapeDebugInner::Char(chr) => {
|
||||
self.clear();
|
||||
Some(chr)
|
||||
}
|
||||
}
|
||||
self.0.next()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -367,10 +347,7 @@ impl Iterator for EscapeDebug {
|
|||
#[stable(feature = "char_escape_debug", since = "1.20.0")]
|
||||
impl ExactSizeIterator for EscapeDebug {
|
||||
fn len(&self) -> usize {
|
||||
match &self.0 {
|
||||
EscapeDebugInner::Bytes(bytes) => bytes.len(),
|
||||
EscapeDebugInner::Char(_) => 1,
|
||||
}
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -379,11 +356,9 @@ impl FusedIterator for EscapeDebug {}
|
|||
|
||||
#[stable(feature = "char_escape_debug", since = "1.20.0")]
|
||||
impl fmt::Display for EscapeDebug {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.0 {
|
||||
EscapeDebugInner::Bytes(bytes) => f.write_str(bytes.as_str()),
|
||||
EscapeDebugInner::Char(chr) => f.write_char(*chr),
|
||||
}
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -480,6 +455,7 @@ macro_rules! casemappingiter_impls {
|
|||
|
||||
#[stable(feature = "char_struct_display", since = "1.16.0")]
|
||||
impl fmt::Display for $ITER_NAME {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
//! Helper code for character escaping.
|
||||
|
||||
use crate::ascii;
|
||||
use crate::fmt::{self, Write};
|
||||
use crate::marker::PhantomData;
|
||||
use crate::num::NonZero;
|
||||
use crate::ops::Range;
|
||||
|
||||
const HEX_DIGITS: [ascii::Char; 16] = *b"0123456789abcdef".as_ascii().unwrap();
|
||||
|
||||
/// Escapes a character with `\x` representation.
|
||||
///
|
||||
/// Returns a buffer with the escaped representation and its corresponding range.
|
||||
#[inline]
|
||||
const fn backslash<const N: usize>(a: ascii::Char) -> ([ascii::Char; N], Range<u8>) {
|
||||
const { assert!(N >= 2) };
|
||||
|
|
@ -18,6 +23,9 @@ const fn backslash<const N: usize>(a: ascii::Char) -> ([ascii::Char; N], Range<u
|
|||
(output, 0..2)
|
||||
}
|
||||
|
||||
/// Escapes a character with `\xNN` representation.
|
||||
///
|
||||
/// Returns a buffer with the escaped representation and its corresponding range.
|
||||
#[inline]
|
||||
const fn hex_escape<const N: usize>(byte: u8) -> ([ascii::Char; N], Range<u8>) {
|
||||
const { assert!(N >= 4) };
|
||||
|
|
@ -35,6 +43,7 @@ const fn hex_escape<const N: usize>(byte: u8) -> ([ascii::Char; N], Range<u8>) {
|
|||
(output, 0..4)
|
||||
}
|
||||
|
||||
/// Returns a buffer with the verbatim character and its corresponding range.
|
||||
#[inline]
|
||||
const fn verbatim<const N: usize>(a: ascii::Char) -> ([ascii::Char; N], Range<u8>) {
|
||||
const { assert!(N >= 1) };
|
||||
|
|
@ -48,7 +57,7 @@ const fn verbatim<const N: usize>(a: ascii::Char) -> ([ascii::Char; N], Range<u8
|
|||
|
||||
/// Escapes an ASCII character.
|
||||
///
|
||||
/// Returns a buffer and the length of the escaped representation.
|
||||
/// Returns a buffer with the escaped representation and its corresponding range.
|
||||
const fn escape_ascii<const N: usize>(byte: u8) -> ([ascii::Char; N], Range<u8>) {
|
||||
const { assert!(N >= 4) };
|
||||
|
||||
|
|
@ -122,9 +131,9 @@ const fn escape_ascii<const N: usize>(byte: u8) -> ([ascii::Char; N], Range<u8>)
|
|||
}
|
||||
}
|
||||
|
||||
/// Escapes a character `\u{NNNN}` representation.
|
||||
/// Escapes a character with `\u{NNNN}` representation.
|
||||
///
|
||||
/// Returns a buffer and the length of the escaped representation.
|
||||
/// Returns a buffer with the escaped representation and its corresponding range.
|
||||
const fn escape_unicode<const N: usize>(c: char) -> ([ascii::Char; N], Range<u8>) {
|
||||
const { assert!(N >= 10 && N < u8::MAX as usize) };
|
||||
|
||||
|
|
@ -149,51 +158,81 @@ const fn escape_unicode<const N: usize>(c: char) -> ([ascii::Char; N], Range<u8>
|
|||
(output, (start as u8)..(N as u8))
|
||||
}
|
||||
|
||||
/// An iterator over an fixed-size array.
|
||||
///
|
||||
/// This is essentially equivalent to array’s IntoIter except that indexes are
|
||||
/// limited to u8 to reduce size of the structure.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct EscapeIterInner<const N: usize> {
|
||||
// The element type ensures this is always ASCII, and thus also valid UTF-8.
|
||||
data: [ascii::Char; N],
|
||||
|
||||
// Invariant: `alive.start <= alive.end <= N`
|
||||
alive: Range<u8>,
|
||||
#[derive(Clone, Copy)]
|
||||
union MaybeEscapedCharacter<const N: usize> {
|
||||
pub escape_seq: [ascii::Char; N],
|
||||
pub literal: char,
|
||||
}
|
||||
|
||||
impl<const N: usize> EscapeIterInner<N> {
|
||||
/// Marker type to indicate that the character is always escaped,
|
||||
/// used to optimize the iterator implementation.
|
||||
#[derive(Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub(crate) struct AlwaysEscaped;
|
||||
|
||||
/// Marker type to indicate that the character may be escaped,
|
||||
/// used to optimize the iterator implementation.
|
||||
#[derive(Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub(crate) struct MaybeEscaped;
|
||||
|
||||
/// An iterator over a possibly escaped character.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct EscapeIterInner<const N: usize, ESCAPING> {
|
||||
// Invariant:
|
||||
//
|
||||
// If `alive.end <= Self::LITERAL_ESCAPE_START`, `data` must contain
|
||||
// printable ASCII characters in the `alive` range of its `escape_seq` variant.
|
||||
//
|
||||
// If `alive.end > Self::LITERAL_ESCAPE_START`, `data` must contain a
|
||||
// `char` in its `literal` variant, and the `alive` range must have a
|
||||
// length of at most `1`.
|
||||
data: MaybeEscapedCharacter<N>,
|
||||
alive: Range<u8>,
|
||||
escaping: PhantomData<ESCAPING>,
|
||||
}
|
||||
|
||||
impl<const N: usize, ESCAPING> EscapeIterInner<N, ESCAPING> {
|
||||
const LITERAL_ESCAPE_START: u8 = 128;
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `data.escape_seq` must contain an escape sequence in the range given by `alive`.
|
||||
#[inline]
|
||||
const unsafe fn new(data: MaybeEscapedCharacter<N>, alive: Range<u8>) -> Self {
|
||||
// Longer escape sequences are not useful given `alive.end` is at most
|
||||
// `Self::LITERAL_ESCAPE_START`.
|
||||
const { assert!(N < Self::LITERAL_ESCAPE_START as usize) };
|
||||
|
||||
// Check bounds, which implicitly also checks the invariant
|
||||
// `alive.end <= Self::LITERAL_ESCAPE_START`.
|
||||
debug_assert!(alive.end <= (N + 1) as u8);
|
||||
|
||||
Self { data, alive, escaping: PhantomData }
|
||||
}
|
||||
|
||||
pub(crate) const fn backslash(c: ascii::Char) -> Self {
|
||||
let (data, range) = backslash(c);
|
||||
Self { data, alive: range }
|
||||
let (escape_seq, alive) = backslash(c);
|
||||
// SAFETY: `escape_seq` contains an escape sequence in the range given by `alive`.
|
||||
unsafe { Self::new(MaybeEscapedCharacter { escape_seq }, alive) }
|
||||
}
|
||||
|
||||
pub(crate) const fn ascii(c: u8) -> Self {
|
||||
let (data, range) = escape_ascii(c);
|
||||
Self { data, alive: range }
|
||||
let (escape_seq, alive) = escape_ascii(c);
|
||||
// SAFETY: `escape_seq` contains an escape sequence in the range given by `alive`.
|
||||
unsafe { Self::new(MaybeEscapedCharacter { escape_seq }, alive) }
|
||||
}
|
||||
|
||||
pub(crate) const fn unicode(c: char) -> Self {
|
||||
let (data, range) = escape_unicode(c);
|
||||
Self { data, alive: range }
|
||||
let (escape_seq, alive) = escape_unicode(c);
|
||||
// SAFETY: `escape_seq` contains an escape sequence in the range given by `alive`.
|
||||
unsafe { Self::new(MaybeEscapedCharacter { escape_seq }, alive) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) const fn empty() -> Self {
|
||||
Self { data: [ascii::Char::Null; N], alive: 0..0 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn as_ascii(&self) -> &[ascii::Char] {
|
||||
// SAFETY: `self.alive` is guaranteed to be a valid range for indexing `self.data`.
|
||||
unsafe {
|
||||
self.data.get_unchecked(usize::from(self.alive.start)..usize::from(self.alive.end))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn as_str(&self) -> &str {
|
||||
self.as_ascii().as_str()
|
||||
// SAFETY: `0..0` ensures an empty escape sequence.
|
||||
unsafe { Self::new(MaybeEscapedCharacter { escape_seq: [ascii::Char::Null; N] }, 0..0) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -201,25 +240,132 @@ impl<const N: usize> EscapeIterInner<N> {
|
|||
usize::from(self.alive.end - self.alive.start)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
|
||||
self.alive.advance_by(n)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
|
||||
self.alive.advance_back_by(n)
|
||||
}
|
||||
|
||||
/// Returns a `char` if `self.data` contains one in its `literal` variant.
|
||||
#[inline]
|
||||
const fn to_char(&self) -> Option<char> {
|
||||
if self.alive.end > Self::LITERAL_ESCAPE_START {
|
||||
// SAFETY: We just checked that `self.data` contains a `char` in
|
||||
// its `literal` variant.
|
||||
return Some(unsafe { self.data.literal });
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the printable ASCII characters in the `escape_seq` variant of `self.data`
|
||||
/// as a string.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - `self.data` must contain printable ASCII characters in its `escape_seq` variant.
|
||||
/// - `self.alive` must be a valid range for `self.data.escape_seq`.
|
||||
#[inline]
|
||||
unsafe fn to_str_unchecked(&self) -> &str {
|
||||
debug_assert!(self.alive.end <= Self::LITERAL_ESCAPE_START);
|
||||
|
||||
// SAFETY: The caller guarantees `self.data` contains printable ASCII
|
||||
// characters in its `escape_seq` variant, and `self.alive` is
|
||||
// a valid range for `self.data.escape_seq`.
|
||||
unsafe {
|
||||
self.data
|
||||
.escape_seq
|
||||
.get_unchecked(usize::from(self.alive.start)..usize::from(self.alive.end))
|
||||
.as_str()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> EscapeIterInner<N, AlwaysEscaped> {
|
||||
pub(crate) fn next(&mut self) -> Option<u8> {
|
||||
let i = self.alive.next()?;
|
||||
|
||||
// SAFETY: `i` is guaranteed to be a valid index for `self.data`.
|
||||
unsafe { Some(self.data.get_unchecked(usize::from(i)).to_u8()) }
|
||||
// SAFETY: The `AlwaysEscaped` marker guarantees that `self.data`
|
||||
// contains printable ASCII characters in its `escape_seq`
|
||||
// variant, and `i` is guaranteed to be a valid index for
|
||||
// `self.data.escape_seq`.
|
||||
unsafe { Some(self.data.escape_seq.get_unchecked(usize::from(i)).to_u8()) }
|
||||
}
|
||||
|
||||
pub(crate) fn next_back(&mut self) -> Option<u8> {
|
||||
let i = self.alive.next_back()?;
|
||||
|
||||
// SAFETY: `i` is guaranteed to be a valid index for `self.data`.
|
||||
unsafe { Some(self.data.get_unchecked(usize::from(i)).to_u8()) }
|
||||
}
|
||||
|
||||
pub(crate) fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
|
||||
self.alive.advance_by(n)
|
||||
}
|
||||
|
||||
pub(crate) fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
|
||||
self.alive.advance_back_by(n)
|
||||
// SAFETY: The `AlwaysEscaped` marker guarantees that `self.data`
|
||||
// contains printable ASCII characters in its `escape_seq`
|
||||
// variant, and `i` is guaranteed to be a valid index for
|
||||
// `self.data.escape_seq`.
|
||||
unsafe { Some(self.data.escape_seq.get_unchecked(usize::from(i)).to_u8()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> EscapeIterInner<N, MaybeEscaped> {
|
||||
// This is the only way to create any `EscapeIterInner` containing a `char` in
|
||||
// the `literal` variant of its `self.data`, meaning the `AlwaysEscaped` marker
|
||||
// guarantees that `self.data` contains printable ASCII characters in its
|
||||
// `escape_seq` variant.
|
||||
pub(crate) const fn printable(c: char) -> Self {
|
||||
Self {
|
||||
data: MaybeEscapedCharacter { literal: c },
|
||||
// Uphold the invariant `alive.end > Self::LITERAL_ESCAPE_START`, and ensure
|
||||
// `len` behaves correctly for iterating through one character literal.
|
||||
alive: Self::LITERAL_ESCAPE_START..(Self::LITERAL_ESCAPE_START + 1),
|
||||
escaping: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn next(&mut self) -> Option<char> {
|
||||
let i = self.alive.next()?;
|
||||
|
||||
if let Some(c) = self.to_char() {
|
||||
return Some(c);
|
||||
}
|
||||
|
||||
// SAFETY: At this point, `self.data` must contain printable ASCII
|
||||
// characters in its `escape_seq` variant, and `i` is
|
||||
// guaranteed to be a valid index for `self.data.escape_seq`.
|
||||
Some(char::from(unsafe { self.data.escape_seq.get_unchecked(usize::from(i)).to_u8() }))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Display for EscapeIterInner<N, AlwaysEscaped> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// SAFETY: The `AlwaysEscaped` marker guarantees that `self.data`
|
||||
// contains printable ASCII chars, and `self.alive` is
|
||||
// guaranteed to be a valid range for `self.data`.
|
||||
f.write_str(unsafe { self.to_str_unchecked() })
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Display for EscapeIterInner<N, MaybeEscaped> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(c) = self.to_char() {
|
||||
return f.write_char(c);
|
||||
}
|
||||
|
||||
// SAFETY: At this point, `self.data` must contain printable ASCII
|
||||
// characters in its `escape_seq` variant, and `self.alive`
|
||||
// is guaranteed to be a valid range for `self.data`.
|
||||
f.write_str(unsafe { self.to_str_unchecked() })
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Debug for EscapeIterInner<N, AlwaysEscaped> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("EscapeIterInner").field(&format_args!("'{}'", self)).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Debug for EscapeIterInner<N, MaybeEscaped> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("EscapeIterInner").field(&format_args!("'{}'", self)).finish()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ impl<'a> fmt::Display for EscapeAscii<'a> {
|
|||
|
||||
if let Some(&b) = bytes.first() {
|
||||
// guaranteed to be non-empty, better to write it as a str
|
||||
f.write_str(ascii::escape_default(b).as_str())?;
|
||||
fmt::Display::fmt(&ascii::escape_default(b), f)?;
|
||||
bytes = &bytes[1..];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,8 @@ Compiletest makes the following replacements on the compiler output:
|
|||
- The base directory where the test's output goes is replaced with
|
||||
`$TEST_BUILD_DIR`. This only comes up in a few rare circumstances. Example:
|
||||
`/path/to/rust/build/x86_64-unknown-linux-gnu/test/ui`
|
||||
- The real directory to the standard library source is replaced with `$SRC_DIR_REAL`.
|
||||
- The real directory to the compiler source is replaced with `$COMPILER_DIR_REAL`.
|
||||
- Tabs are replaced with `\t`.
|
||||
- Backslashes (`\`) are converted to forward slashes (`/`) within paths (using a
|
||||
heuristic). This helps normalize differences with Windows-style paths.
|
||||
|
|
|
|||
|
|
@ -471,6 +471,9 @@ to customize the output:
|
|||
- `future-incompat` - includes a JSON message that contains a report if the
|
||||
crate contains any code that may fail to compile in the future.
|
||||
|
||||
- `timings` - output a JSON message when a certain compilation "section"
|
||||
(such as frontend analysis, code generation, linking) begins or ends.
|
||||
|
||||
Note that it is invalid to combine the `--json` argument with the
|
||||
[`--color`](#option-color) argument, and it is required to combine `--json`
|
||||
with `--error-format=json`.
|
||||
|
|
|
|||
|
|
@ -298,6 +298,35 @@ appropriately. (This is needed by Cargo which shares the same dependencies
|
|||
across multiple build targets, so it should only report an unused dependency if
|
||||
its not used by any of the targets.)
|
||||
|
||||
## Timings
|
||||
|
||||
**This setting is currently unstable and requires usage of `-Zunstable-options`.**
|
||||
|
||||
The `--timings` option will tell `rustc` to emit messages when a certain compilation
|
||||
section (such as code generation or linking) begins or ends. The messages currently have
|
||||
the following format:
|
||||
|
||||
```json
|
||||
{
|
||||
"$message_type": "section_timing", /* Type of this message */
|
||||
"event": "start", /* Marks the "start" or "end" of the compilation section */
|
||||
"name": "link", /* The name of the compilation section */
|
||||
// Opaque timestamp when the message was emitted, in microseconds
|
||||
// The timestamp is currently relative to the beginning of the compilation session
|
||||
"time": 12345
|
||||
}
|
||||
```
|
||||
|
||||
Note that the JSON format of the `timings` messages is unstable and subject to change.
|
||||
|
||||
Compilation sections can be nested; for example, if you encounter the start of "foo",
|
||||
then the start of "bar", then the end of "bar" and then the end of "bar", it means that the
|
||||
"bar" section happened as a part of the "foo" section.
|
||||
|
||||
The timestamp should only be used for computing the duration of each section.
|
||||
|
||||
We currently do not guarantee any specific section names to be emitted.
|
||||
|
||||
[option-emit]: command-line-arguments.md#option-emit
|
||||
[option-error-format]: command-line-arguments.md#option-error-format
|
||||
[option-json]: command-line-arguments.md#option-json
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
# `hint-mostly-unused`
|
||||
|
||||
This flag hints to the compiler that most of the crate will probably go unused.
|
||||
The compiler can optimize its operation based on this assumption, in order to
|
||||
compile faster. This is a hint, and does not guarantee any particular behavior.
|
||||
|
||||
This option can substantially speed up compilation if applied to a large
|
||||
dependency where the majority of the dependency does not get used. This flag
|
||||
may slow down compilation in other cases.
|
||||
|
||||
Currently, this option makes the compiler defer as much code generation as
|
||||
possible from functions in the crate, until later crates invoke those
|
||||
functions. Functions that never get invoked will never have code generated for
|
||||
them. For instance, if a crate provides thousands of functions, but only a few
|
||||
of them will get called, this flag will result in the compiler only doing code
|
||||
generation for the called functions. (This uses the same mechanisms as
|
||||
cross-crate inlining of functions.) This does not affect `extern` functions, or
|
||||
functions marked as `#[inline(never)]`.
|
||||
|
||||
To try applying this flag to one dependency out of a dependency tree, use the
|
||||
[`profile-rustflags`](https://doc.rust-lang.org/cargo/reference/unstable.html#profile-rustflags-option)
|
||||
feature of nightly cargo:
|
||||
|
||||
```toml
|
||||
cargo-features = ["profile-rustflags"]
|
||||
|
||||
# ...
|
||||
[dependencies]
|
||||
mostly-unused-dependency = "1.2.3"
|
||||
|
||||
[profile.release.package.mostly-unused-dependency]
|
||||
rustflags = ["-Zhint-mostly-unused"]
|
||||
```
|
||||
|
|
@ -2372,6 +2372,12 @@ impl<'test> TestCx<'test> {
|
|||
rust_src_dir.read_link_utf8().unwrap_or_else(|_| rust_src_dir.to_path_buf());
|
||||
normalize_path(&rust_src_dir.join("library"), "$SRC_DIR_REAL");
|
||||
|
||||
// Real paths into the compiler
|
||||
let rustc_src_dir = &self.config.sysroot_base.join("lib/rustlib/rustc-src/rust");
|
||||
rustc_src_dir.try_exists().expect(&*format!("{} should exists", rustc_src_dir));
|
||||
let rustc_src_dir = rustc_src_dir.read_link_utf8().unwrap_or(rustc_src_dir.to_path_buf());
|
||||
normalize_path(&rustc_src_dir.join("compiler"), "$COMPILER_DIR_REAL");
|
||||
|
||||
// eg.
|
||||
// /home/user/rust/build/x86_64-unknown-linux-gnu/test/ui/<test_dir>/$name.$revision.$mode/
|
||||
normalize_path(&self.output_base_dir(), "$TEST_BUILD_DIR");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
//@ compile-flags: -Z unstable-options
|
||||
//@ ignore-stage1
|
||||
|
||||
#![feature(rustc_private)]
|
||||
#![deny(rustc::direct_use_of_rustc_type_ir)]
|
||||
|
||||
extern crate rustc_middle;
|
||||
extern crate rustc_type_ir;
|
||||
|
||||
use rustc_middle::ty::*; // OK, we have to accept rustc_middle::ty::*
|
||||
|
||||
// We have to deny direct import of type_ir
|
||||
use rustc_type_ir::*;
|
||||
//~^ ERROR: do not use `rustc_type_ir` unless you are implementing type system internals
|
||||
|
||||
// We have to deny direct types usages which resolves to type_ir
|
||||
fn foo<I: rustc_type_ir::Interner>(cx: I, did: I::DefId) {
|
||||
//~^ ERROR: do not use `rustc_type_ir` unless you are implementing type system internals
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = rustc_type_ir::InferConst::Fresh(42);
|
||||
//~^ ERROR: do not use `rustc_type_ir` unless you are implementing type system internals
|
||||
let _: rustc_type_ir::InferConst;
|
||||
//~^ ERROR: do not use `rustc_type_ir` unless you are implementing type system internals
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
error: do not use `rustc_type_ir` unless you are implementing type system internals
|
||||
--> $DIR/direct-use-of-rustc-type-ir.rs:13:5
|
||||
|
|
||||
LL | use rustc_type_ir::*;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: use `rustc_middle::ty` instead
|
||||
note: the lint level is defined here
|
||||
--> $DIR/direct-use-of-rustc-type-ir.rs:5:9
|
||||
|
|
||||
LL | #![deny(rustc::direct_use_of_rustc_type_ir)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: do not use `rustc_type_ir` unless you are implementing type system internals
|
||||
--> $DIR/direct-use-of-rustc-type-ir.rs:17:11
|
||||
|
|
||||
LL | fn foo<I: rustc_type_ir::Interner>(cx: I, did: I::DefId) {
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: use `rustc_middle::ty` instead
|
||||
|
||||
error: do not use `rustc_type_ir` unless you are implementing type system internals
|
||||
--> $DIR/direct-use-of-rustc-type-ir.rs:22:13
|
||||
|
|
||||
LL | let _ = rustc_type_ir::InferConst::Fresh(42);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: use `rustc_middle::ty` instead
|
||||
|
||||
error: do not use `rustc_type_ir` unless you are implementing type system internals
|
||||
--> $DIR/direct-use-of-rustc-type-ir.rs:24:12
|
||||
|
|
||||
LL | let _: rustc_type_ir::InferConst;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: use `rustc_middle::ty` instead
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
15
tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr
Normal file
15
tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
error[E0277]: the trait bound `NotAValidResultType: VisitorResult` is not satisfied
|
||||
--> $DIR/rustc-dev-remap.rs:LL:COL
|
||||
|
|
||||
LL | type Result = NotAValidResultType;
|
||||
| ^^^^^^^^^^^^^^^^^^^ the trait `VisitorResult` is not implemented for `NotAValidResultType`
|
||||
|
|
||||
= help: the following other types implement trait `VisitorResult`:
|
||||
()
|
||||
ControlFlow<T>
|
||||
note: required by a bound in `rustc_ast::visit::Visitor::Result`
|
||||
--> /rustc-dev/xyz/compiler/rustc_ast/src/visit.rs:LL:COL
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
18
tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr
Normal file
18
tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
error[E0277]: the trait bound `NotAValidResultType: VisitorResult` is not satisfied
|
||||
--> $DIR/rustc-dev-remap.rs:LL:COL
|
||||
|
|
||||
LL | type Result = NotAValidResultType;
|
||||
| ^^^^^^^^^^^^^^^^^^^ the trait `VisitorResult` is not implemented for `NotAValidResultType`
|
||||
|
|
||||
= help: the following other types implement trait `VisitorResult`:
|
||||
()
|
||||
ControlFlow<T>
|
||||
note: required by a bound in `rustc_ast::visit::Visitor::Result`
|
||||
--> $COMPILER_DIR_REAL/rustc_ast/src/visit.rs:LL:COL
|
||||
|
|
||||
LL | type Result: VisitorResult = ();
|
||||
| ^^^^^^^^^^^^^ required by this bound in `Visitor::Result`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
30
tests/ui-fulldeps/rustc-dev-remap.rs
Normal file
30
tests/ui-fulldeps/rustc-dev-remap.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//@ check-fail
|
||||
//
|
||||
//@ ignore-stage1
|
||||
//@ ignore-cross-compile
|
||||
//@ ignore-remote
|
||||
//
|
||||
//@ revisions: only-remap remap-unremap
|
||||
//@ compile-flags: -Z simulate-remapped-rust-src-base=/rustc-dev/xyz
|
||||
//@ [remap-unremap]compile-flags: -Ztranslate-remapped-path-to-local-path=yes
|
||||
|
||||
// The $SRC_DIR*.rs:LL:COL normalisation doesn't kick in automatically
|
||||
// as the remapped revision will begin with $COMPILER_DIR_REAL,
|
||||
// so we have to do it ourselves.
|
||||
//@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:COL"
|
||||
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate rustc_ast;
|
||||
|
||||
use rustc_ast::visit::Visitor;
|
||||
|
||||
struct MyStruct;
|
||||
struct NotAValidResultType;
|
||||
|
||||
impl Visitor<'_> for MyStruct {
|
||||
type Result = NotAValidResultType;
|
||||
//~^ ERROR the trait bound `NotAValidResultType: VisitorResult` is not satisfied
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue