Skip to content

Commit cec81e7

Browse files
committed
rustc: support overriding region printing in ty::print::Printer.
1 parent c80c4d3 commit cec81e7

File tree

5 files changed

+199
-137
lines changed

5 files changed

+199
-137
lines changed

src/librustc/infer/error_reporting/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
458458
type Error = NonTrivialPath;
459459

460460
type Path = Vec<String>;
461+
type Region = !;
462+
463+
fn print_region(
464+
self: PrintCx<'_, '_, '_, Self>,
465+
_region: ty::Region<'_>,
466+
) -> Result<Self::Region, Self::Error> {
467+
Err(NonTrivialPath)
468+
}
461469

462470
fn path_crate(
463471
self: PrintCx<'_, '_, '_, Self>,

src/librustc/ty/print.rs

Lines changed: 164 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use hir::def::Namespace;
22
use hir::map::DefPathData;
33
use hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
4+
use middle::region;
45
use ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable};
56
use ty::subst::{Kind, Subst, Substs, UnpackedKind};
67
use middle::cstore::{ExternCrate, ExternCrateSource};
@@ -68,7 +69,7 @@ pub struct RegionHighlightMode {
6869
/// This is used when you have a signature like `fn foo(x: &u32,
6970
/// y: &'a u32)` and we want to give a name to the region of the
7071
/// reference `x`.
71-
pub(crate) highlight_bound_region: Option<(ty::BoundRegion, usize)>,
72+
highlight_bound_region: Option<(ty::BoundRegion, usize)>,
7273
}
7374

7475
impl RegionHighlightMode {
@@ -116,7 +117,7 @@ impl RegionHighlightMode {
116117

117118
/// Returns `Some(n)` with the number to use for the given region,
118119
/// if any.
119-
pub(crate) fn region_highlighted(&self, region: ty::Region<'_>) -> Option<usize> {
120+
fn region_highlighted(&self, region: ty::Region<'_>) -> Option<usize> {
120121
self
121122
.highlight_regions
122123
.iter()
@@ -252,6 +253,7 @@ pub trait Printer: Sized {
252253
type Error;
253254

254255
type Path;
256+
type Region;
255257

256258
fn print_def_path(
257259
self: PrintCx<'_, '_, 'tcx, Self>,
@@ -273,6 +275,11 @@ pub trait Printer: Sized {
273275
self.default_print_impl_path(impl_def_id, substs, ns, self_ty, trait_ref)
274276
}
275277

278+
fn print_region(
279+
self: PrintCx<'_, '_, '_, Self>,
280+
region: ty::Region<'_>,
281+
) -> Result<Self::Region, Self::Error>;
282+
276283
fn path_crate(
277284
self: PrintCx<'_, '_, '_, Self>,
278285
cnum: CrateNum,
@@ -312,7 +319,7 @@ pub trait Printer: Sized {
312319
}
313320

314321
/// Trait for printers that pretty-print using `fmt::Write` to the printer.
315-
pub trait PrettyPrinter: Printer<Error = fmt::Error, Path = Self> + fmt::Write {
322+
pub trait PrettyPrinter: Printer<Error = fmt::Error, Path = Self, Region = Self> + fmt::Write {
316323
/// Enter a nested print context, for pretty-printing
317324
/// nested components in some larger context.
318325
fn nest<'a, 'gcx, 'tcx, E>(
@@ -331,9 +338,26 @@ pub trait PrettyPrinter: Printer<Error = fmt::Error, Path = Self> + fmt::Write {
331338
})
332339
}
333340

334-
fn region_highlight_mode(&self) -> RegionHighlightMode {
335-
RegionHighlightMode::default()
341+
/// Return `true` if the region should be printed in path generic args
342+
/// even when it's `'_`, such as in e.g. `Foo<'_, '_, '_>`.
343+
fn always_print_region_in_paths(
344+
self: &PrintCx<'_, '_, '_, Self>,
345+
_region: ty::Region<'_>,
346+
) -> bool {
347+
false
336348
}
349+
350+
// HACK(eddyb) Trying to print a lifetime might not print anything, which
351+
// may need special handling in the caller (of `ty::RegionKind::print`).
352+
// To avoid printing to a temporary string (which isn't even supported),
353+
// the `print_region_outputs_anything` method can instead be used to
354+
// determine this, ahead of time.
355+
//
356+
// NB: this must be kept in sync with the implementation of `print_region`.
357+
fn print_region_outputs_anything(
358+
self: &PrintCx<'_, '_, '_, Self>,
359+
region: ty::Region<'_>,
360+
) -> bool;
337361
}
338362

339363
macro_rules! nest {
@@ -799,10 +823,13 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
799823

800824
let start = if ns == Namespace::ValueNS { "::<" } else { "<" };
801825

802-
// Don't print any regions if they're all erased.
826+
// Don't print `'_` if there's no printed region.
803827
let print_regions = params.iter().any(|param| {
804828
match substs[param.index as usize].unpack() {
805-
UnpackedKind::Lifetime(r) => *r != ty::ReErased,
829+
UnpackedKind::Lifetime(r) => {
830+
self.always_print_region_in_paths(r) ||
831+
self.print_region_outputs_anything(r)
832+
}
806833
_ => false,
807834
}
808835
});
@@ -830,7 +857,7 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
830857
continue;
831858
}
832859
start_or_continue(&mut self, start, ", ")?;
833-
if !region.display_outputs_anything(&self) {
860+
if !self.print_region_outputs_anything(region) {
834861
// This happens when the value of the region
835862
// parameter is not easily serialized. This may be
836863
// because the user omitted it in the first place,
@@ -872,6 +899,7 @@ impl<F: fmt::Write> Printer for FmtPrinter<F> {
872899
type Error = fmt::Error;
873900

874901
type Path = Self;
902+
type Region = Self;
875903

876904
fn print_def_path(
877905
mut self: PrintCx<'_, '_, 'tcx, Self>,
@@ -928,6 +956,80 @@ impl<F: fmt::Write> Printer for FmtPrinter<F> {
928956
self.default_print_def_path(def_id, substs, ns, projections)
929957
}
930958

959+
fn print_region(
960+
mut self: PrintCx<'_, '_, '_, Self>,
961+
region: ty::Region<'_>,
962+
) -> Result<Self::Region, Self::Error> {
963+
// Watch out for region highlights.
964+
let highlight = self.printer.region_highlight_mode;
965+
if let Some(n) = highlight.region_highlighted(region) {
966+
write!(self.printer, "'{}", n)?;
967+
return Ok(self.printer);
968+
}
969+
970+
if self.config.is_verbose {
971+
return region.print_debug(self);
972+
}
973+
974+
// These printouts are concise. They do not contain all the information
975+
// the user might want to diagnose an error, but there is basically no way
976+
// to fit that into a short string. Hence the recommendation to use
977+
// `explain_region()` or `note_and_explain_region()`.
978+
match *region {
979+
ty::ReEarlyBound(ref data) => {
980+
if data.name != "'_" {
981+
write!(self.printer, "{}", data.name)?;
982+
}
983+
}
984+
ty::ReLateBound(_, br) |
985+
ty::ReFree(ty::FreeRegion { bound_region: br, .. }) |
986+
ty::RePlaceholder(ty::Placeholder { name: br, .. }) => {
987+
if let ty::BrNamed(_, name) = br {
988+
if name != "" && name != "'_" {
989+
write!(self.printer, "{}", name)?;
990+
return Ok(self.printer);
991+
}
992+
}
993+
994+
if let Some((region, counter)) = highlight.highlight_bound_region {
995+
if br == region {
996+
write!(self.printer, "'{}", counter)?;
997+
}
998+
}
999+
}
1000+
ty::ReScope(scope) if self.config.identify_regions => {
1001+
match scope.data {
1002+
region::ScopeData::Node =>
1003+
write!(self.printer, "'{}s", scope.item_local_id().as_usize())?,
1004+
region::ScopeData::CallSite =>
1005+
write!(self.printer, "'{}cs", scope.item_local_id().as_usize())?,
1006+
region::ScopeData::Arguments =>
1007+
write!(self.printer, "'{}as", scope.item_local_id().as_usize())?,
1008+
region::ScopeData::Destruction =>
1009+
write!(self.printer, "'{}ds", scope.item_local_id().as_usize())?,
1010+
region::ScopeData::Remainder(first_statement_index) => write!(self.printer,
1011+
"'{}_{}rs",
1012+
scope.item_local_id().as_usize(),
1013+
first_statement_index.index()
1014+
)?,
1015+
}
1016+
}
1017+
ty::ReVar(region_vid) if self.config.identify_regions => {
1018+
write!(self.printer, "{:?}", region_vid)?;
1019+
}
1020+
ty::ReVar(_) => {}
1021+
ty::ReScope(_) |
1022+
ty::ReErased => {}
1023+
ty::ReStatic => write!(self.printer, "'static")?,
1024+
ty::ReEmpty => write!(self.printer, "'<empty>")?,
1025+
1026+
// The user should never encounter these in unsubstituted form.
1027+
ty::ReClosureBound(vid) => write!(self.printer, "{:?}", vid)?,
1028+
}
1029+
1030+
Ok(self.printer)
1031+
}
1032+
9311033
fn path_crate(
9321034
mut self: PrintCx<'_, '_, '_, Self>,
9331035
cnum: CrateNum,
@@ -1017,7 +1119,59 @@ impl<F: fmt::Write> PrettyPrinter for FmtPrinter<F> {
10171119
})
10181120
}
10191121

1020-
fn region_highlight_mode(&self) -> RegionHighlightMode {
1021-
self.region_highlight_mode
1122+
fn always_print_region_in_paths(
1123+
self: &PrintCx<'_, '_, '_, Self>,
1124+
region: ty::Region<'_>,
1125+
) -> bool {
1126+
*region != ty::ReErased
1127+
}
1128+
1129+
fn print_region_outputs_anything(
1130+
self: &PrintCx<'_, '_, '_, Self>,
1131+
region: ty::Region<'_>,
1132+
) -> bool {
1133+
let highlight = self.printer.region_highlight_mode;
1134+
if highlight.region_highlighted(region).is_some() {
1135+
return true;
1136+
}
1137+
1138+
if self.config.is_verbose {
1139+
return true;
1140+
}
1141+
1142+
match *region {
1143+
ty::ReEarlyBound(ref data) => {
1144+
data.name != "" && data.name != "'_"
1145+
}
1146+
1147+
ty::ReLateBound(_, br) |
1148+
ty::ReFree(ty::FreeRegion { bound_region: br, .. }) |
1149+
ty::RePlaceholder(ty::Placeholder { name: br, .. }) => {
1150+
if let ty::BrNamed(_, name) = br {
1151+
if name != "" && name != "'_" {
1152+
return true;
1153+
}
1154+
}
1155+
1156+
if let Some((region, _)) = highlight.highlight_bound_region {
1157+
if br == region {
1158+
return true;
1159+
}
1160+
}
1161+
1162+
false
1163+
}
1164+
1165+
ty::ReScope(_) |
1166+
ty::ReVar(_) if self.config.identify_regions => true,
1167+
1168+
ty::ReVar(_) |
1169+
ty::ReScope(_) |
1170+
ty::ReErased => false,
1171+
1172+
ty::ReStatic |
1173+
ty::ReEmpty |
1174+
ty::ReClosureBound(_) => true,
1175+
}
10221176
}
10231177
}

0 commit comments

Comments
 (0)